Merge pull request #363 from presenton/refactor/templates

fix: clean up unused code in presentation templates
This commit is contained in:
Shiva Raj Badu 2025-11-28 01:02:09 +05:45 committed by GitHub
commit fb84032da4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
32 changed files with 1288 additions and 1567 deletions

View file

@ -35,7 +35,7 @@ const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slide
return (
<>
{/* Import Google Fonts */}
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
@ -44,19 +44,9 @@ const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slide
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Main Content */}
@ -80,20 +70,20 @@ const BasicInfoSlideLayout: React.FC<BasicInfoSlideLayoutProps> = ({ data: slide
</h1>
{/* Purple accent line */}
<div style={{background:"var(--text-heading-color,#9333ea)"}} className="w-20 h-1 bg-purple-600"></div>
<div style={{ background: "var(--text-heading-color,#9333ea)" }} className="w-20 h-1 bg-purple-600"></div>
{/* Description */}
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base sm:text-lg text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base sm:text-lg text-gray-700 leading-relaxed">
{slideData?.description || 'Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.'}
</p>
</div>
</div>
</div>
</>
)

View file

@ -91,26 +91,17 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
/>
<div
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{/* {(slideData as any)?.__companyName__ && ( */}
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
{/* )} */}
{/* Decorative Wave Patterns */}
<div className="absolute top-0 left-0 w-32 h-full opacity-10 overflow-hidden">
<svg className="w-full h-full" viewBox="0 0 100 400" fill="none">
@ -142,7 +133,7 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
className={`flex items-start space-x-4 p-4 rounded-lg`}
>
{/* Icon */}
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center">
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
@ -154,11 +145,11 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
{/* Content */}
<div className="flex-1">
<h3 style={{color:"var(--text-heading-color,#111827)"}} className="text-lg sm:text-xl font-semibold text-gray-900 mb-1">
<h3 style={{ color: "var(--text-heading-color,#111827)" }} className="text-lg sm:text-xl font-semibold text-gray-900 mb-1">
{bullet.title}
</h3>
{bullet.subtitle && (
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-sm text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-sm text-gray-700 leading-relaxed">
{bullet.subtitle}
</p>
)}
@ -171,14 +162,14 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
{/* Right Section - Image */}
<div className="flex-shrink-0 w-96 flex items-center justify-center relative">
{/* Decorative Elements */}
<div style={{color:"var(--primary-accent-color,#9333ea)"}} className="absolute top-8 right-8 text-purple-600 opacity-60">
<div style={{ color: "var(--primary-accent-color,#9333ea)" }} className="absolute top-8 right-8 text-purple-600 opacity-60">
<svg width="32" height="32" viewBox="0 0 32 32" fill="currentColor">
<path d="M16 0l4.12 8.38L28 12l-7.88 3.62L16 24l-4.12-8.38L4 12l7.88-3.62L16 0z" />
</svg>
</div>
<div className="absolute top-16 left-8 opacity-20">
<svg width="80" height="20" viewBox="0 0 80 20" className="text-purple-600" style={{color:"var(--primary-accent-color,#9333ea)"}}>
<svg width="80" height="20" viewBox="0 0 80 20" className="text-purple-600" style={{ color: "var(--primary-accent-color,#9333ea)" }}>
<path
d="M0 10 Q20 0 40 10 T80 10"
stroke="currentColor"

View file

@ -63,28 +63,19 @@ const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({
return (
<>
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div
<div
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-gradient-to-br from-gray-50 to-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Main Content */}
@ -100,71 +91,71 @@ const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({
<div className="flex flex-1">
{/* Left Section - Image with Grid Pattern */}
<div className="flex-1 relative">
{/* Grid Pattern Background */}
<div className="absolute top-0 left-0 w-full h-full">
<svg className="w-full h-full opacity-30" viewBox="0 0 200 200">
<defs>
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--primary-accent-color,#9333ea)" strokeWidth="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
</div>
{/* Image Container */}
<div className="relative z-10 h-full flex items-center justify-center p-4">
<div className="w-full max-w-md h-80 rounded-2xl overflow-hidden shadow-lg">
<img
src={slideData?.image?.__image_url__ || ''}
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
className="w-full h-full object-cover"
/>
{/* Grid Pattern Background */}
<div className="absolute top-0 left-0 w-full h-full">
<svg className="w-full h-full opacity-30" viewBox="0 0 200 200">
<defs>
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="var(--primary-accent-color,#9333ea)" strokeWidth="0.5" />
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
</div>
{/* Image Container */}
<div className="relative z-10 h-full flex items-center justify-center p-4">
<div className="w-full max-w-md h-80 rounded-2xl overflow-hidden shadow-lg">
<img
src={slideData?.image?.__image_url__ || ''}
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
className="w-full h-full object-cover"
/>
</div>
</div>
{/* Decorative Sparkle */}
<div style={{ color: "var(--primary-accent-color,#9333ea)" }} className="absolute top-20 right-8 text-purple-600">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0l3.09 6.26L22 9l-6.91 2.74L12 18l-3.09-6.26L2 9l6.91-2.74L12 0z" />
</svg>
</div>
</div>
{/* Decorative Sparkle */}
<div style={{color:"var(--primary-accent-color,#9333ea)"}} className="absolute top-20 right-8 text-purple-600">
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 0l3.09 6.26L22 9l-6.91 2.74L12 18l-3.09-6.26L2 9l6.91-2.74L12 0z"/>
</svg>
</div>
</div>
{/* Right Section - Content */}
<div className="flex-1 flex flex-col justify-center pl-8 lg:pl-16">
{/* Description */}
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-lg text-gray-700 leading-relaxed mb-8">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-lg text-gray-700 leading-relaxed mb-8">
{slideData?.description || 'Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.'}
</p>
{/* Bullet Points */}
<div className="space-y-6">
{bulletPoints.map((bullet, index) => (
<div key={index} className="flex items-start space-x-4">
{/* Icon */}
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="flex-shrink-0 w-12 h-12 rounded-lg shadow-md flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--text-heading-color,#ffffff)"
title={bullet.icon.__icon_query__}
/>
{/* Bullet Points */}
<div className="space-y-6">
{bulletPoints.map((bullet, index) => (
<div key={index} className="flex items-start space-x-4">
{/* Icon */}
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="flex-shrink-0 w-12 h-12 rounded-lg shadow-md flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--text-heading-color,#ffffff)"
title={bullet.icon.__icon_query__}
/>
</div>
{/* Content */}
<div className="flex-1">
<h3 style={{ color: "var(--text-heading-color,#111827)" }} className="text-xl font-semibold text-gray-900 mb-2">
{bullet.title}
</h3>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-12 h-0.5 bg-purple-600 mb-3"></div>
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base text-gray-700 leading-relaxed">
{bullet.description}
</p>
</div>
</div>
{/* Content */}
<div className="flex-1">
<h3 style={{color:"var(--text-heading-color,#111827)"}} className="text-xl font-semibold text-gray-900 mb-2">
{bullet.title}
</h3>
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-12 h-0.5 bg-purple-600 mb-3"></div>
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base text-gray-700 leading-relaxed">
{bullet.description}
</p>
</div>
</div>
))}
))}
</div>
</div>
</div>

View file

@ -231,8 +231,8 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
return (
<>
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
@ -241,19 +241,10 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Main Content */}
<div className="flex h-full px-8 sm:px-12 lg:px-20 pt-8 pb-8">
{/* Left Section - Title, Description, Chart */}
@ -264,12 +255,12 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
</h1>
{/* Description */}
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base text-gray-700 leading-relaxed mb-8">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base text-gray-700 leading-relaxed mb-8">
{slideData?.description || 'Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.'}
</p>
{/* Chart Container */}
<div className="flex-1 rounded-lg shadow-sm border border-gray-100 p-4" style={{ background: 'var(--primary-accent-color,#F5F8FE)' }}>
<div className="flex-1 rounded-lg shadow-sm border border-gray-100 p-4" style={{ background: 'var(--primary-accent-color,#F5F8FE)' }}>
<ChartContainer config={chartConfig} className="h-full w-full">
{renderChart()}
</ChartContainer>
@ -288,7 +279,7 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
>
{/* Icon and Title */}
<div className="flex items-center space-x-3 mb-3">
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-8 h-8 rounded-lg flex items-center justify-center">
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-8 h-8 rounded-lg flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
@ -297,13 +288,13 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
title={bullet.icon.__icon_query__}
/>
</div>
<h3 style={{color:"var(--text-heading-color,#ffffff)"}} className="text-lg font-semibold">
<h3 style={{ color: "var(--text-heading-color,#ffffff)" }} className="text-lg font-semibold">
{bullet.title}
</h3>
</div>
{/* Description */}
<p style={{color:"var(--text-body-color,#ffffff)"}} className="text-sm leading-relaxed opacity-90">
<p style={{ color: "var(--text-body-color,#ffffff)" }} className="text-sm leading-relaxed opacity-90">
{bullet.description}
</p>
</div>

View file

@ -44,28 +44,19 @@ const IntroSlideLayout: React.FC<IntroSlideLayoutProps> = ({ data: slideData })
const presenterInitials = getInitials(slideData?.presenterName || 'John Doe');
return (
<>
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div
<div
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
background:"var(--card-background-color,#ffffff)"
,fontFamily:"var(--heading-font-family,Inter)"
background: "var(--card-background-color,#ffffff)"
, fontFamily: "var(--heading-font-family,Inter)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Main Content */}
@ -89,29 +80,29 @@ const IntroSlideLayout: React.FC<IntroSlideLayoutProps> = ({ data: slideData })
</h1>
{/* Purple accent line */}
<div style={{background:"var(--text-heading-color,#9333ea)"}} className="w-20 h-1 bg-purple-600"></div>
<div style={{ background: "var(--text-heading-color,#9333ea)" }} className="w-20 h-1 bg-purple-600"></div>
{/* Description */}
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base sm:text-lg text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base sm:text-lg text-gray-700 leading-relaxed">
{slideData?.description || 'Our product offers customizable dashboards for real-time reporting and data-driven decisions. It integrates with third-party tools to enhance operations and scales with business growth for improved efficiency.'}
</p>
{/* Presenter Section */}
<div style={{background:"var(--card-background-color,rgb(255 255 255 / 0.5))"}} className="bg-white/50 backdrop-blur-sm rounded-lg p-4 lg:p-6 border border-gray-200 shadow-sm">
<div style={{ background: "var(--card-background-color,rgb(255 255 255 / 0.5))" }} className="bg-white/50 backdrop-blur-sm rounded-lg p-4 lg:p-6 border border-gray-200 shadow-sm">
<div className="flex items-center gap-4">
{/* Custom Initials Icon */}
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-10 h-10 lg:w-12 lg:h-12 bg-purple-600 rounded-full flex items-center justify-center">
<span className="font-bold text-sm lg:text-base" style={{color:"var(--text-heading-color,#FFFFFF)"}}>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-10 h-10 lg:w-12 lg:h-12 bg-purple-600 rounded-full flex items-center justify-center">
<span className="font-bold text-sm lg:text-base" style={{ color: "var(--text-heading-color,#FFFFFF)" }}>
{presenterInitials}
</span>
</div>
{/* Presenter Info */}
<div className="flex flex-col">
<span style={{color:"var(--text-heading-color,#111827)"}} className="text-lg lg:text-xl font-bold text-gray-900">
<span style={{ color: "var(--text-heading-color,#111827)" }} className="text-lg lg:text-xl font-bold text-gray-900">
{slideData?.presenterName || 'John Doe'}
</span>
<span style={{color:"var(--text-body-color,#4b5563)"}} className="text-sm lg:text-base text-gray-600 font-medium">
<span style={{ color: "var(--text-body-color,#4b5563)" }} className="text-sm lg:text-base text-gray-600 font-medium">
{slideData?.presentationDate || 'December 2024'}
</span>
</div>

View file

@ -77,7 +77,7 @@ const MetricsSlideLayout: React.FC<MetricsSlideLayoutProps> = ({ data: slideData
return (
<>
{/* Import Google Fonts */}
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
@ -86,19 +86,10 @@ const MetricsSlideLayout: React.FC<MetricsSlideLayoutProps> = ({ data: slideData
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden flex flex-col"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Decorative Wave Patterns */}
<div className="absolute top-0 left-0 w-64 h-full opacity-10 overflow-hidden">
<svg className="w-full h-full" viewBox="0 0 200 400" fill="none">
@ -135,22 +126,22 @@ const MetricsSlideLayout: React.FC<MetricsSlideLayoutProps> = ({ data: slideData
{metrics.map((metric, index) => (
<div key={index} className={`text-center space-y-4 ${getItemClasses(metrics.length)}`}>
{/* Label */}
<div className="text-sm text-gray-600 font-medium" style={{color:"var(--text-body-color,#ffffff)"}}>
<div className="text-sm text-gray-600 font-medium" style={{ color: "var(--text-body-color,#ffffff)" }}>
{metric.label}
</div>
{/* Large Metric Value */}
<div style={{color:"var(--text-heading-color,#9333ea)"}} className="text-4xl sm:text-5xl lg:text-6xl font-bold text-purple-600">
<div style={{ color: "var(--text-heading-color,#9333ea)" }} className="text-4xl sm:text-5xl lg:text-6xl font-bold text-purple-600">
{metric.value}
</div>
{/* Description Box */}
<div
className="bg-purple-50 rounded-lg p-4 lg:p-5 text-center mt-4"
style={{background:"var(--primary-accent-color,#9333ea)"}}
style={{ background: "var(--primary-accent-color,#9333ea)" }}
>
<p style={{color:"var(--text-body-color,#ffffff)"}} className="text-xs sm:text-sm text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#ffffff)" }} className="text-xs sm:text-sm text-gray-700 leading-relaxed">
{metric.description}
</p>
</div>

View file

@ -7,7 +7,7 @@ export const layoutName = 'Metrics with Image'
export const layoutDescription = 'A slide layout with supporting image on the left and title, description, and metrics grid on the right. Can be used alternatively with MetricSlide.'
const metricsWithImageSlideSchema = z.object({
title: z.string().min(3).max(40).default('Competitive Advantage').meta({
title: z.string().min(3).max(40).default('Competitive Advantage').meta({
description: "Main title of the slide",
}),
description: z.string().min(10).max(150).default('Ginyard International Co. stands out by offering custom digital solutions tailored to client needs, alongside long-term support to ensure lasting relationships and continuous adaptation.').meta({
@ -35,7 +35,7 @@ const metricsWithImageSlideSchema = z.object({
label: 'Client Retention Rate',
value: '95%'
},
]).meta({
description: "List of key business metrics to display",
})
@ -58,35 +58,26 @@ const MetricsWithImageSlideLayout: React.FC<MetricsWithImageSlideLayoutProps> =
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div
<div
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Decorative Wave Patterns */}
<div className="absolute bottom-0 left-0 w-48 h-48 opacity-10 overflow-hidden">
<svg className="w-full h-full" viewBox="0 0 200 200" fill="none">
<path d="M0 100C50 75 100 125 150 100C175 87.5 200 100 200 100V200H0V100Z" fill="#8b5cf6" opacity="0.4"/>
<path d="M0 150C75 175 125 125 200 150V175C150 162.5 100 175 50 162.5L0 150Z" fill="#8b5cf6" opacity="0.3"/>
<path d="M0 100C50 75 100 125 150 100C175 87.5 200 100 200 100V200H0V100Z" fill="#8b5cf6" opacity="0.4" />
<path d="M0 150C75 175 125 125 200 150V175C150 162.5 100 175 50 162.5L0 150Z" fill="#8b5cf6" opacity="0.3" />
</svg>
</div>
<div className="absolute top-0 right-0 w-64 h-64 opacity-10 overflow-hidden">
<svg className="w-full h-full" viewBox="0 0 200 200" fill="none">
<path d="M100 0C150 50 200 0 200 50C200 100 150 150 100 150C50 150 0 100 0 50C0 0 50 50 100 0Z" fill="#8b5cf6" opacity="0.2"/>
<path d="M100 0C150 50 200 0 200 50C200 100 150 150 100 150C50 150 0 100 0 50C0 0 50 50 100 0Z" fill="#8b5cf6" opacity="0.2" />
</svg>
</div>
@ -111,7 +102,7 @@ const MetricsWithImageSlideLayout: React.FC<MetricsWithImageSlideLayoutProps> =
</h1>
{/* Description */}
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base sm:text-lg text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base sm:text-lg text-gray-700 leading-relaxed">
{slideData?.description || 'Ginyard International Co. stands out by offering custom digital solutions tailored to client needs, alongside long-term support to ensure lasting relationships and continuous adaptation.'}
</p>
@ -119,10 +110,10 @@ const MetricsWithImageSlideLayout: React.FC<MetricsWithImageSlideLayoutProps> =
<div className="grid grid-cols-2 gap-6">
{metrics.map((metric, index) => (
<div key={index} className="text-center space-y-2">
<div style={{color:"var(--text-body-color,#4b5563)"}} className="text-sm text-gray-600 font-medium">
<div style={{ color: "var(--text-body-color,#4b5563)" }} className="text-sm text-gray-600 font-medium">
{metric.label}
</div>
<div style={{color:"var(--text-heading-color,#9333ea)"}} className="text-3xl sm:text-4xl lg:text-5xl font-bold text-purple-600">
<div style={{ color: "var(--text-heading-color,#9333ea)" }} className="text-3xl sm:text-4xl lg:text-5xl font-bold text-purple-600">
{metric.value}
</div>
</div>

View file

@ -58,7 +58,7 @@ const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({
return (
<>
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
@ -67,19 +67,10 @@ const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Main Content Container */}
<div className="px-8 sm:px-12 lg:px-20 pt-12 pb-8 h-full">
@ -91,7 +82,7 @@ const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({
{slideData?.title || 'Market Validation'}
</h1>
{/* Purple accent line */}
<div style={{background:"var(--text-heading-color,#9333ea)"}} className="w-24 h-1 bg-purple-600 mb-6"></div>
<div style={{ background: "var(--text-heading-color,#9333ea)" }} className="w-24 h-1 bg-purple-600 mb-6"></div>
</div>
{/* Image Section */}
@ -99,7 +90,7 @@ const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({
<img
src={slideData?.image?.__image_url__ || ''}
alt={slideData?.image?.__image_prompt__ || slideData?.title || ''}
className="w-full h-full object-cover rounded-lg shadow-md" style={{background:"var(--tertiary-accent-color,#e5e7eb)"}}
className="w-full h-full object-cover rounded-lg shadow-md" style={{ background: "var(--tertiary-accent-color,#e5e7eb)" }}
/>
</div>
</div>
@ -110,17 +101,17 @@ const NumberedBulletsSlideLayout: React.FC<NumberedBulletsSlideLayoutProps> = ({
<div key={index} className="flex items-start space-x-4">
{/* Number */}
<div className="flex-shrink-0">
<div style={{color:"var(--text-heading-color,#111827)"}} className="text-4xl sm:text-5xl font-bold text-gray-900">
<div style={{ color: "var(--text-heading-color,#111827)" }} className="text-4xl sm:text-5xl font-bold text-gray-900">
{String(index + 1).padStart(2, '0')}
</div>
</div>
{/* Content */}
<div className="flex-1 pt-2">
<h3 style={{color:"var(--text-heading-color,#111827)"}} className="text-xl sm:text-2xl font-bold text-gray-900 mb-3">
<h3 style={{ color: "var(--text-heading-color,#111827)" }} className="text-xl sm:text-2xl font-bold text-gray-900 mb-3">
{bullet.title}
</h3>
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base text-gray-700 leading-relaxed">
{bullet.description}
</p>
</div>

View file

@ -45,19 +45,10 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #ffffff)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #ffffff)' }}></div>
</div>
</div>
)}
{/* Background Image */}
<div
className="absolute inset-0 w-full h-full bg-cover bg-center bg-no-repeat"
@ -87,7 +78,7 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
{slideData?.heading || 'Words of Wisdom'}
</h1>
{/* Purple accent line */}
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-20 h-1 bg-purple-400 mx-auto"></div>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-20 h-1 bg-purple-400 mx-auto"></div>
</div>
{/* Quote Section */}
@ -95,7 +86,7 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
{/* Quote Icon */}
<div className="flex justify-center">
<svg
className="w-12 h-12 text-purple-300 opacity-80" style={{color:"var(--primary-accent-color,#9333ea)"}}
className="w-12 h-12 text-purple-300 opacity-80" style={{ color: "var(--primary-accent-color,#9333ea)" }}
fill="currentColor"
viewBox="0 0 24 24"
>
@ -104,17 +95,17 @@ const QuoteSlideLayout: React.FC<QuoteSlideLayoutProps> = ({ data: slideData })
</div>
{/* Quote Text */}
<blockquote style={{color:"var(--text-body-color,#ffffff)"}} className="text-xl sm:text-2xl lg:text-3xl font-medium text-white leading-relaxed italic">
<blockquote style={{ color: "var(--text-body-color,#ffffff)" }} className="text-xl sm:text-2xl lg:text-3xl font-medium text-white leading-relaxed italic">
"{slideData?.quote || 'Success is not final, failure is not fatal: it is the courage to continue that counts. The future belongs to those who believe in the beauty of their dreams.'}"
</blockquote>
{/* Author */}
<div className="flex justify-center items-center space-x-4">
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-16 h-px bg-purple-300"></div>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-16 h-px bg-purple-300"></div>
<cite className="text-base sm:text-lg text-purple-200 font-semibold not-italic">
{slideData?.author || 'Winston Churchill'}
</cite>
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-16 h-px bg-purple-300"></div>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-16 h-px bg-purple-300"></div>
</div>
</div>
</div>

View file

@ -61,19 +61,10 @@ const TableInfoSlideLayout: React.FC<TableInfoSlideLayoutProps> = ({ data: slide
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden flex flex-col"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Decorative Wave Patterns */}
<div className="absolute top-0 left-0 w-64 h-full opacity-10 overflow-hidden">
<svg className="w-full h-full" viewBox="0 0 200 400" fill="none">
@ -93,14 +84,14 @@ const TableInfoSlideLayout: React.FC<TableInfoSlideLayoutProps> = ({ data: slide
{/* Main Content */}
<div className="relative z-10 px-8 sm:px-12 lg:px-20 pt-12 py-8 flex-1 flex flex-col justify-between">
{/* Title Section */}
<div className="text-center space-y-4">
<h1 style={{ color: "var(--text-heading-color,#111827)" }} className="text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900">
{slideData?.title || 'Market Comparison'}
</h1>
{/* Purple accent line */}
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-20 h-1 bg-purple-600 mx-auto"></div>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-20 h-1 bg-purple-600 mx-auto"></div>
</div>
{/* Table Section */}
@ -111,7 +102,7 @@ const TableInfoSlideLayout: React.FC<TableInfoSlideLayoutProps> = ({ data: slide
<div style={{ backgroundColor: "var(--primary-accent-color,#9333ea)" }}>
<div className="grid gap-px" style={{ gridTemplateColumns: `repeat(${tableHeaders.length}, 1fr)` }}>
{tableHeaders.map((header, index) => (
<div key={index} className="px-6 py-4 font-semibold text-center text-sm sm:text-base" style={{color:"var(--text-heading-color,#111827)"}}>
<div key={index} className="px-6 py-4 font-semibold text-center text-sm sm:text-base" style={{ color: "var(--text-heading-color,#111827)" }}>
{header}
</div>
))}
@ -121,8 +112,8 @@ const TableInfoSlideLayout: React.FC<TableInfoSlideLayoutProps> = ({ data: slide
{/* Table Body */}
<div className="divide-y divide-gray-200">
{tableRows.map((row, rowIndex) => (
<div
key={rowIndex}
<div
key={rowIndex}
className={`grid gap-px ${rowIndex % 2 === 0 ? 'bg-gray-50' : 'bg-white'} transition-colors duration-200`}
style={{ gridTemplateColumns: `repeat(${tableHeaders.length}, 1fr)` }}
>
@ -145,13 +136,13 @@ const TableInfoSlideLayout: React.FC<TableInfoSlideLayoutProps> = ({ data: slide
</div>
</div>
</div>
</div>
{/* Description Section */}
<div className="text-center space-y-4">
<div className="max-w-4xl mx-auto">
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-sm sm:text-base text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-sm sm:text-base text-gray-700 leading-relaxed">
{slideData?.description || 'This comparison shows our competitive position in the market. While we currently have a smaller market share, our growth rate significantly exceeds competitors, indicating strong potential for future expansion.'}
</p>
</div>

View file

@ -48,28 +48,19 @@ const TableOfContentsSlideLayout: React.FC<TableOfContentsSlideLayoutProps> = ({
return (
<>
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div
<div
className="w-full rounded-sm max-w-[1280px] shadow-lg px-8 sm:px-12 lg:px-20 py-8 sm:py-12 lg:py-16 max-h-[720px] aspect-video bg-white relative z-20 mx-auto"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Title Section */}
<div className="text-center mb-8 sm:mb-12 mt-6">
@ -78,7 +69,7 @@ const TableOfContentsSlideLayout: React.FC<TableOfContentsSlideLayoutProps> = ({
</h1>
{/* Decorative Wave */}
<div className="flex justify-center">
<svg width="80" height="20" viewBox="0 0 80 20" className="text-purple-600" style={{color:"var(--primary-accent-color,#9333ea)"}}>
<svg width="80" height="20" viewBox="0 0 80 20" className="text-purple-600" style={{ color: "var(--primary-accent-color,#9333ea)" }}>
<path
d="M0 10 Q20 0 40 10 T80 10"
stroke="currentColor"
@ -97,21 +88,21 @@ const TableOfContentsSlideLayout: React.FC<TableOfContentsSlideLayoutProps> = ({
<div key={section.number} className="flex items-center justify-between group">
<div className="flex items-center space-x-4">
{/* Number Box */}
<div style={{background:"var(--primary-accent-color,#9333ea)", color:"var(--text-heading-color,#ffffff)"}} className="w-12 h-12 sm:w-14 sm:h-14 bg-purple-600 rounded-xl flex items-center justify-center text-white font-bold text-lg sm:text-xl group-hover:bg-purple-700 transition-colors">
<div style={{ background: "var(--primary-accent-color,#9333ea)", color: "var(--text-heading-color,#ffffff)" }} className="w-12 h-12 sm:w-14 sm:h-14 bg-purple-600 rounded-xl flex items-center justify-center text-white font-bold text-lg sm:text-xl group-hover:bg-purple-700 transition-colors">
{section.number}
</div>
{/* Title */}
<span style={{color:"var(--text-heading-color,#111827)"}} className="text-lg sm:text-xl font-medium text-gray-800 group-hover:text-purple-600 transition-colors">
<span style={{ color: "var(--text-heading-color,#111827)" }} className="text-lg sm:text-xl font-medium text-gray-800 group-hover:text-purple-600 transition-colors">
{section.title}
</span>
</div>
{/* Page Number */}
<div className="text-right">
<span style={{color:"var(--text-body-color,#4b5563)"}} className="text-lg sm:text-xl text-gray-600">
<span style={{ color: "var(--text-body-color,#4b5563)" }} className="text-lg sm:text-xl text-gray-600">
{section.pageNumber}
</span>
{/* Dotted line effect */}
<div style={{color:"var(--text-body-color,#4b5563)"}} className="text-gray-300 text-sm mt-1">
<div style={{ color: "var(--text-body-color,#4b5563)" }} className="text-gray-300 text-sm mt-1">
.....
</div>
</div>
@ -125,21 +116,21 @@ const TableOfContentsSlideLayout: React.FC<TableOfContentsSlideLayoutProps> = ({
<div key={section.number} className="flex items-center justify-between group">
<div className="flex items-center space-x-4">
{/* Number Box */}
<div style={{background:"var(--primary-accent-color,#9333ea)", color:"var(--text-heading-color,#ffffff)"}} className="w-12 h-12 sm:w-14 sm:h-14 bg-purple-600 rounded-xl flex items-center justify-center text-white font-bold text-lg sm:text-xl group-hover:bg-purple-700 transition-colors">
<div style={{ background: "var(--primary-accent-color,#9333ea)", color: "var(--text-heading-color,#ffffff)" }} className="w-12 h-12 sm:w-14 sm:h-14 bg-purple-600 rounded-xl flex items-center justify-center text-white font-bold text-lg sm:text-xl group-hover:bg-purple-700 transition-colors">
{section.number}
</div>
{/* Title */}
<span style={{color:"var(--text-heading-color,#111827)"}} className="text-lg sm:text-xl font-medium text-gray-800 group-hover:text-purple-600 transition-colors">
<span style={{ color: "var(--text-heading-color,#111827)" }} className="text-lg sm:text-xl font-medium text-gray-800 group-hover:text-purple-600 transition-colors">
{section.title}
</span>
</div>
{/* Page Number */}
<div className="text-right">
<span style={{color:"var(--text-body-color,#4b5563)"}} className="text-lg sm:text-xl text-gray-600">
<span style={{ color: "var(--text-body-color,#4b5563)" }} className="text-lg sm:text-xl text-gray-600">
{section.pageNumber}
</span>
{/* Dotted line effect */}
<div style={{color:"var(--text-body-color,#4b5563)"}} className="text-gray-300 text-sm mt-1">
<div style={{ color: "var(--text-body-color,#4b5563)" }} className="text-gray-300 text-sm mt-1">
.....
</div>
</div>

View file

@ -92,28 +92,19 @@ const TeamSlideLayout: React.FC<TeamSlideLayoutProps> = ({ data: slideData }) =>
return (
<>
<link
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div
className="w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden"
style={{
fontFamily: 'var(--heading-font-family,Inter)',
background:"var(--card-background-color,#ffffff)"
background: "var(--card-background-color,#ffffff)"
}}
>
{(slideData as any)?.__companyName__ && (
<div className="absolute top-0 left-0 right-0 px-8 sm:px-12 lg:px-20 pt-4">
<div className="flex items-center gap-4">
<span className="text-sm sm:text-base font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Company Name'}
</span>
<div className="h-[2px] flex-1 opacity-70" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
)}
{/* Decorative Wave Pattern */}
<div className="absolute bottom-0 left-0 w-80 h-40 opacity-10 overflow-hidden">
<svg className="w-full h-full" viewBox="0 0 300 150" fill="none">
@ -132,10 +123,10 @@ const TeamSlideLayout: React.FC<TeamSlideLayoutProps> = ({ data: slideData }) =>
</h1>
{/* Purple accent line */}
<div style={{background:"var(--primary-accent-color,#9333ea)"}} className="w-20 h-1 bg-purple-600"></div>
<div style={{ background: "var(--primary-accent-color,#9333ea)" }} className="w-20 h-1 bg-purple-600"></div>
{/* Company Description */}
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-base sm:text-lg text-gray-700 leading-relaxed">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-base sm:text-lg text-gray-700 leading-relaxed">
{slideData?.companyDescription || 'Ginyard International Co. is a leading provider of innovative digital solutions tailored for businesses. Our mission is to empower organizations to achieve their goals through cutting-edge technology and strategic partnerships.'}
</p>
</div>
@ -146,7 +137,7 @@ const TeamSlideLayout: React.FC<TeamSlideLayoutProps> = ({ data: slideData }) =>
{teamMembers.map((member, index) => (
<div key={index} className="text-center space-y-3">
{/* Member Photo */}
<div className="w-32 h-32 mx-auto rounded-lg overflow-hidden shadow-md" style={{background:"var(--tertiary-accent-color,#e5e7eb)"}}>
<div className="w-32 h-32 mx-auto rounded-lg overflow-hidden shadow-md" style={{ background: "var(--tertiary-accent-color,#e5e7eb)" }}>
<img
src={member.image.__image_url__ || ''}
alt={member.image.__image_prompt__ || member.name}
@ -156,13 +147,13 @@ const TeamSlideLayout: React.FC<TeamSlideLayoutProps> = ({ data: slideData }) =>
{/* Member Info */}
<div>
<h3 style={{color:"var(--text-heading-color,#111827)"}} className="text-lg font-semibold text-gray-900">
<h3 style={{ color: "var(--text-heading-color,#111827)" }} className="text-lg font-semibold text-gray-900">
{member.name}
</h3>
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-sm font-medium text-gray-600 italic mb-2">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-sm font-medium text-gray-600 italic mb-2">
{member.position}
</p>
<p style={{color:"var(--text-body-color,#4b5563)"}} className="text-xs text-gray-600 leading-relaxed px-2">
<p style={{ color: "var(--text-body-color,#4b5563)" }} className="text-xs text-gray-600 leading-relaxed px-2">
{member.description}
</p>
</div>

View file

@ -26,7 +26,7 @@ const ChartDatumSchema = z.object({
})
const Schema = z.object({
title: z
.string()
.min(16)
@ -92,19 +92,9 @@ const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) =>
/>
<div
className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden"
style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}
style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}
>
<div className="w-full flex items-center justify-between px-10 pt-6">
<div className="flex items-center gap-5">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || 'Pitchdeck'}
</span>}
<svg className="w-[220px] h-[2px]" viewBox="0 0 220 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="220" height="2" style={{ fill: 'var(--text-heading-color, #111827)' }}></rect>
</svg>
</div>
{/* page number intentionally omitted */}
</div>
<div className="grid grid-cols-2 h-[calc(100%-64px)]">
{/* Left: Recharts visualization */}

View file

@ -1,167 +1,159 @@
import React from 'react'
import * as z from "zod";
import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(120).default("Muted cover background image with subtle subject suitable for contact slide").meta({
description: "Prompt used to generate the image. Max 24 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("data:svg+xml,placeholder").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(24).default("photo placeholder").meta({
description: "Query used to search the icon. Max 5 words",
}),
})
const layoutId = "header-left-media-contact-info-slide"
const layoutName = "ContactLayout"
const layoutDescription = "A slide with a top bar, left media with overlay and bottom bar, and right content with header and text blocks. This should only be used for contact information."
const Schema = z.object({
metaMaxWords: z.number().default(24).meta({
description: "Maximum number of words in any single text field",
}),
topBar: z.object({
pageNumber: z.string().min(1).max(3).default("9").meta({
description: "Top-right number text. Max 1 word",
}),
__image_prompt__: z.string().min(10).max(120).default("Muted cover background image with subtle subject suitable for contact slide").meta({
description: "Prompt used to generate the image. Max 24 words",
}).default({
pageNumber: "9",
}),
leftPanel: z.object({
backgroundImage: ImageSchema.default({
__image_url__: "https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg",
__image_prompt__: "Muted cover background image with subtle subject suitable for contact slide",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("data:svg+xml,placeholder").meta({
description: "URL to icon",
centerIcon: IconSchema.default({
__icon_url__: "data:svg+xml,placeholder",
__icon_query__: "photo placeholder",
}),
__icon_query__: z.string().min(3).max(24).default("photo placeholder").meta({
description: "Query used to search the icon. Max 5 words",
}),
})
const layoutId = "header-left-media-contact-info-slide"
const layoutName = "ContactLayout"
const layoutDescription = "A slide with a top bar, left media with overlay and bottom bar, and right content with header and text blocks. This should only be used for contact information."
const Schema = z.object({
metaMaxWords: z.number().default(24).meta({
description: "Maximum number of words in any single text field",
}),
topBar: z.object({
pageNumber: z.string().min(1).max(3).default("9").meta({
description: "Top-right number text. Max 1 word",
websiteBar: z.object({
websiteText: z.string().min(12).max(30).default("www.yourwebsite.com").meta({
description: "Website text in bottom green bar. Max 4 words",
}),
actionIcon: IconSchema.default({
__icon_url__: "data:svg+xml,plus-arrow",
__icon_query__: "plus arrow",
}),
}).default({
pageNumber: "9",
}),
leftPanel: z.object({
backgroundImage: ImageSchema.default({
__image_url__: "https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg",
__image_prompt__: "Muted cover background image with subtle subject suitable for contact slide",
}),
centerIcon: IconSchema.default({
__icon_url__: "data:svg+xml,placeholder",
__icon_query__: "photo placeholder",
}),
websiteBar: z.object({
websiteText: z.string().min(12).max(30).default("www.yourwebsite.com").meta({
description: "Website text in bottom green bar. Max 4 words",
}),
actionIcon: IconSchema.default({
__icon_url__: "data:svg+xml,plus-arrow",
__icon_query__: "plus arrow",
}),
}).default({
websiteText: "www.yourwebsite.com",
actionIcon: {
__icon_url__: "data:svg+xml,plus-arrow",
__icon_query__: "plus arrow",
},
}),
}).default({
backgroundImage: {
__image_url__: "https://images.pexels.com/photos/326576/pexels-photo-326576.jpeg",
__image_prompt__: "Muted cover background image with subtle subject suitable for contact slide",
},
centerIcon: {
__icon_url__: "data:svg+xml,placeholder",
__icon_query__: "photo placeholder",
},
websiteBar: {
websiteText: "www.yourwebsite.com",
actionIcon: {
__icon_url__: "data:svg+xml,plus-arrow",
__icon_query__: "plus arrow",
},
websiteText: "www.yourwebsite.com",
actionIcon: {
__icon_url__: "data:svg+xml,plus-arrow",
__icon_query__: "plus arrow",
},
}),
rightContent: z.object({
title: z.string().min(18).max(40).default("Lets Get in\nTouch with Us").meta({
description: "Main heading. Max 6 words",
}),
sections: z.array(z.object({
label: z.string().min(4).max(10).default("Label").meta({
description: "Section label text. Max 2 words",
}),
value: z.string().min(8).max(50).default("Value text").meta({
description: "Section value text. Max 100 characters",
}),
showDivider: z.boolean().default(true).meta({
description: "Whether to show bottom divider",
}),
})).min(1).max(3).default([
{
label: "Address",
value: "Boston, Downtown Main Street 233, New York, US",
showDivider: true,
},
{
label: "Phone",
value: "+1234 2345 1234",
showDivider: true,
},
{
label: "E-mail:",
value: "mail@company.com",
showDivider: false,
},
]).meta({
description: "List of content sections",
}),
}).default({
title: "Lets Get in\nTouch with Us",
sections: [
{
label: "Address",
value: "Boston, Downtown Main Street 233, New York, US",
showDivider: true,
},
{
label: "Phone",
value: "+1234 2345 1234",
showDivider: true,
},
{
label: "E-mail:",
value: "mail@company.com",
showDivider: false,
},
],
}).default({
backgroundImage: {
__image_url__: "https://images.pexels.com/photos/326576/pexels-photo-326576.jpeg",
__image_prompt__: "Muted cover background image with subtle subject suitable for contact slide",
},
centerIcon: {
__icon_url__: "data:svg+xml,placeholder",
__icon_query__: "photo placeholder",
},
websiteBar: {
websiteText: "www.yourwebsite.com",
actionIcon: {
__icon_url__: "data:svg+xml,plus-arrow",
__icon_query__: "plus arrow",
},
},
}),
rightContent: z.object({
title: z.string().min(18).max(40).default("Lets Get in\nTouch with Us").meta({
description: "Main heading. Max 6 words",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const sections = slideData?.rightContent?.sections || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="w-full flex items-center justify-between px-10 pt-6">
<div className="flex items-center gap-6">
{(slideData as any)?.__companyName__ && <div className="text-[18px] font-semibold tracking-wide" style={{ color: 'var(--primary-accent-color, #1B8C2D)' }}>
{(slideData as any)?.__companyName__ || "Pitchdeck"}
</div>}
<div className="h-[2px] w-[220px] rounded-full" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)' }}></div>
</div>
{/* page number removed */}
</div>
sections: z.array(z.object({
label: z.string().min(4).max(10).default("Label").meta({
description: "Section label text. Max 2 words",
}),
value: z.string().min(8).max(50).default("Value text").meta({
description: "Section value text. Max 100 characters",
}),
showDivider: z.boolean().default(true).meta({
description: "Whether to show bottom divider",
}),
})).min(1).max(3).default([
{
label: "Address",
value: "Boston, Downtown Main Street 233, New York, US",
showDivider: true,
},
{
label: "Phone",
value: "+1234 2345 1234",
showDivider: true,
},
{
label: "E-mail:",
value: "mail@company.com",
showDivider: false,
},
]).meta({
description: "List of content sections",
}),
}).default({
title: "Lets Get in\nTouch with Us",
sections: [
{
label: "Address",
value: "Boston, Downtown Main Street 233, New York, US",
showDivider: true,
},
{
label: "Phone",
value: "+1234 2345 1234",
showDivider: true,
},
{
label: "E-mail:",
value: "mail@company.com",
showDivider: false,
},
],
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const sections = slideData?.rightContent?.sections || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-10 pt-4 pb-8 h-[calc(100%-72px)]">
<div className="grid grid-cols-[640px_1fr] gap-x-16 h-full">
<div className="relative h-full overflow-hidden" style={{ backgroundColor: 'var(--tertiary-accent-color, #E5E7EB)' }}>
@ -171,12 +163,12 @@ const ImageSchema = z.object({
className="absolute inset-0 w-full h-full object-cover"
/>
</div>
<div className="h-full flex flex-col">
<h1 className=" text-[64px] leading-[1.12] font-semibold mb-8 whitespace-pre-line" style={{ color: 'var(--text-heading-color, #111827)' }}>
{slideData?.rightContent?.title || "Lets Get in\nTouch with Us"}
</h1>
<div className="mb-6">
{sections.map((sec, idx) => (
<div key={idx} className={idx < sections.length - 1 ? "mb-6" : "mt-2"}>
@ -190,15 +182,15 @@ const ImageSchema = z.object({
</div>
))}
</div>
<div className="flex-1"></div>
</div>
</div>
</div>
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -3,83 +3,83 @@ import React from 'react'
import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(150).default("Small decorative photo partially behind the card showing a business theme").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("").meta({
description: "Query used to search the icon. Max 5 words",
}),
})
const layoutId = "split-left-strip-header-title-subtitle-cards-slide"
const layoutName = "Heading Bullet Image Description"
const layoutDescription = "A slide with a left strip, top label with rule, right header, right description, floating small image, and a centered card with ...cards."
const Schema = z.object({
metaMaxWords: z.number().default(200).meta({
description: "Maximum number of words the text areas can handle collectively.",
}),
pageNumber: z.string().min(1).max(3).default("7").meta({
description: "Top-right page number text. Max 3 chars",
}),
heading: z.string().min(16).max(38).default("A Blueprint for\nSuccess").meta({
description: "Main heading across up to 2 lines. Max 7 words",
}),
subheading: z.string().min(60).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna").meta({
description: "Supporting paragraph under the heading. Max 35 words",
}),
smallImage: ImageSchema.default({
__image_url__: "https://images.pexels.com/photos/327533/pexels-photo-327533.jpeg",
__image_prompt__: "A small landscape image suitable for a business slide"
}).meta({
description: "Small image partially behind the main card",
}),
cards: z.array(z.object({
title: z.string().min(8).max(16).default("Strategy 01").meta({
description: "Card ribbon title. Max 3 words",
}),
__image_prompt__: z.string().min(10).max(150).default("Small decorative photo partially behind the card showing a business theme").meta({
description: "Prompt used to generate the image. Max 30 words",
body: z.string().min(60).max(160).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor").meta({
description: "Card body text. Max 28 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("").meta({
description: "Query used to search the icon. Max 5 words",
}),
})
const layoutId = "split-left-strip-header-title-subtitle-cards-slide"
const layoutName = "Heading Bullet Image Description"
const layoutDescription = "A slide with a left strip, top label with rule, right header, right description, floating small image, and a centered card with ...cards."
const Schema = z.object({
metaMaxWords: z.number().default(200).meta({
description: "Maximum number of words the text areas can handle collectively.",
}),
pageNumber: z.string().min(1).max(3).default("7").meta({
description: "Top-right page number text. Max 3 chars",
}),
heading: z.string().min(16).max(38).default("A Blueprint for\nSuccess").meta({
description: "Main heading across up to 2 lines. Max 7 words",
}),
subheading: z.string().min(60).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna").meta({
description: "Supporting paragraph under the heading. Max 35 words",
}),
smallImage: ImageSchema.default({
__image_url__: "https://images.pexels.com/photos/327533/pexels-photo-327533.jpeg",
__image_prompt__: "A small landscape image suitable for a business slide"
}).meta({
description: "Small image partially behind the main card",
}),
cards: z.array(z.object({
title: z.string().min(8).max(16).default("Strategy 01").meta({
description: "Card ribbon title. Max 3 words",
}),
body: z.string().min(60).max(160).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor").meta({
description: "Card body text. Max 28 words",
}),
})).min(1).max(4).default([
{ title: "Strategy 01", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
{ title: "Strategy 02", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
{ title: "Strategy 03", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
{ title: "Strategy 04", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
]).meta({
description: "Array of strategy cards",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
})).min(1).max(4).default([
{ title: "Strategy 01", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
{ title: "Strategy 02", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
{ title: "Strategy 03", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
{ title: "Strategy 04", body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor" },
]).meta({
description: "Array of strategy cards",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
{/* page number removed */}
<div className="grid grid-cols-2 h-full">
<div className="relative bg-[#efefef]">
{slideData?.smallImage?.__image_url__ ? (
@ -92,17 +92,10 @@ const ImageSchema = z.object({
{/* overlay removed */}
</>
) : null}
<div className="pt-6 pl-10 pr-6 relative z-[1]">
<div className="flex items-center gap-6">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || "Pitchdeck"}
</span>}
<div className="h-[2px] w-[220px] rounded-full" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
</div>
<div className="absolute bottom-0 left-0 w-full h-[88px] bg-gradient-to-t from-black/20 to-transparent z-[1]"></div>
</div>
<div className="relative px-12 pt-16">
<h1 className=" leading-[72px] text-[64px] tracking-tight whitespace-pre-line font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{slideData?.heading || "A Blueprint for\nSuccess"}
@ -114,24 +107,24 @@ const ImageSchema = z.object({
<div className="w-fit max-w-full rounded-md shadow-[0_20px_60px_rgba(0,0,0,0.12)] ml-auto" style={{ backgroundColor: 'var(--secondary-accent-color, #F3F4F6)' }}>
<div className="px-8 py-10">
<div className="grid grid-flow-col auto-cols-max gap-6">
{cards.map((card, idx) => (
{cards.map((card, idx) => (
<div key={idx} className="flex flex-col items-center">
<div className="w-[240px] h-[64px] rounded-sm text-white flex items-center justify-center text-[22px]" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)', color: 'var(--text-heading-color, #FFFFFF)' }}>
{card.title}
</div>
{card.title}
</div>
<p className="mt-6 text-center text-[16px] leading-[28px] max-w-[240px]" style={{ color: 'var(--text-body-color, #6B7280)' }}>
{card.body}
</p>
</div>
))}
{card.body}
</p>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -5,118 +5,110 @@ import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(180).default("Decorative abstract office scene photo placed at lower right on the band").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/fediverse-logo-bold.svg").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(2).max(20).default("info icon").meta({
description: "Query used to search the icon. Max 3 words",
}),
})
const layoutId = "header-bullets-title-description-image-slide"
const layoutName = "Icon Bullet Description"
const layoutDescription = "A slide with a small header label and number, a left card of ...cards with round symbols and titles with descriptions, a large heading with supporting text, and a decorative image on a mid-page band"
const Schema = z.object({
metaMaxWords: z.number().default(240).meta({
description: "Maximum number of words any single text field can handle in this layout",
}),
headerNumber: z.string().min(1).max(3).default("6").meta({
description: "Small header number text. Max 3 characters",
}),
rightTitle: z.string().min(24).max(72).default("Disrupting the\nIndustry").meta({
description: "Large heading on the right. Max 8 words",
}),
rightDescription: z.string().min(120).max(240).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna").meta({
description: "Supporting paragraph under the large heading. Max 40 words",
}),
// decorative image removed
cards: z.array(z.object({
symbolText: z.string().min(1).max(1).default("i").meta({
description: "Single-character symbol inside the round badge",
}),
__image_prompt__: z.string().min(10).max(180).default("Decorative abstract office scene photo placed at lower right on the band").meta({
description: "Prompt used to generate the image. Max 30 words",
symbolIcon: IconSchema.default({
__icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/fediverse-logo-bold.png",
__icon_query__: "info icon",
}).meta({
description: "Optional icon representation for the round symbol",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/fediverse-logo-bold.svg").meta({
description: "URL to icon",
title: z.string().min(16).max(38).default("Visionary Leadership").meta({
description: "Title for the card item. Max 4 words",
}),
__icon_query__: z.string().min(2).max(20).default("info icon").meta({
description: "Query used to search the icon. Max 3 words",
description: z.string().min(50).max(100).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor").meta({
description: "Description for the card item. Max 15 words.",
}),
})
const layoutId = "header-bullets-title-description-image-slide"
const layoutName = "Icon Bullet Description"
const layoutDescription = "A slide with a small header label and number, a left card of ...cards with round symbols and titles with descriptions, a large heading with supporting text, and a decorative image on a mid-page band"
const Schema = z.object({
metaMaxWords: z.number().default(240).meta({
description: "Maximum number of words any single text field can handle in this layout",
}),
headerNumber: z.string().min(1).max(3).default("6").meta({
description: "Small header number text. Max 3 characters",
}),
rightTitle: z.string().min(24).max(72).default("Disrupting the\nIndustry").meta({
description: "Large heading on the right. Max 8 words",
}),
rightDescription: z.string().min(120).max(240).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna").meta({
description: "Supporting paragraph under the large heading. Max 40 words",
}),
// decorative image removed
cards: z.array(z.object({
symbolText: z.string().min(1).max(1).default("i").meta({
description: "Single-character symbol inside the round badge",
}),
symbolIcon: IconSchema.default({
__icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/fediverse-logo-bold.png",
__icon_query__: "info icon",
}).meta({
description: "Optional icon representation for the round symbol",
}),
title: z.string().min(16).max(38).default("Visionary Leadership").meta({
description: "Title for the card item. Max 4 words",
}),
description: z.string().min(50).max(100).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor").meta({
description: "Description for the card item. Max 15 words.",
}),
})).min(1).max(4).default([
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/fediverse-logo-bold.svg", __icon_query__: "info icon" },
title: "Visionary Leadership",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
},
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/video-bold.png", __icon_query__: "info icon" },
title: "Innovation at the Core",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
},
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/receipt-x-bold.png", __icon_query__: "info icon" },
title: "Customer-Centric Disruption",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
},
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/users-four-bold.png", __icon_query__: "info icon" },
title: "Customer-Centric Disruption",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
}
]).meta({
description: "Array of ...cards with a round symbol, title and description. Max 6 items",
}),
// chart and diagram removed
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
// charts removed
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="relative z-10 flex items-center justify-between px-[40px] pt-[24px]">
<div className="flex items-center gap-[24px]">
{ (slideData as any)?.__companyName__ && <span className=" text-[18px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>{(slideData as any)?.__companyName__ || "Pitchdeck"}</span>}
<svg className="hidden md:block" width="220" height="2" viewBox="0 0 220 2" fill="none" aria-hidden="true">
<rect width="220" height="2" rx="1" style={{ fill: 'var(--text-heading-color, #111827)' }}></rect>
</svg>
</div>
{/* page number removed */}
</div>
})).min(1).max(4).default([
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/fediverse-logo-bold.svg", __icon_query__: "info icon" },
title: "Visionary Leadership",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
},
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/video-bold.png", __icon_query__: "info icon" },
title: "Innovation at the Core",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
},
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/receipt-x-bold.png", __icon_query__: "info icon" },
title: "Customer-Centric Disruption",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
},
{
symbolText: "i",
symbolIcon: { __icon_url__: "https://presenton-public.s3.ap-southeast-1.amazonaws.com/static/icons/bold/users-four-bold.png", __icon_query__: "info icon" },
title: "Customer-Centric Disruption",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor",
}
]).meta({
description: "Array of ...cards with a round symbol, title and description. Max 6 items",
}),
// chart and diagram removed
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
// charts removed
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="relative z-10 grid grid-cols-[600px_1fr] gap-[64px] px-[80px] pt-[12px]">
<div className="w-[600px]">
<div className="rounded-[10px] shadow-[0_20px_60px_rgba(0,0,0,0.08)] px-[36px] py-[28px]" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
@ -126,12 +118,12 @@ const ImageSchema = z.object({
<div className="w-[88px] h-[88px] rounded-full flex items-center justify-center overflow-hidden select-none" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
{item.symbolIcon?.__icon_url__ ? (
<RemoteSvgIcon
url={item.symbolIcon.__icon_url__}
strokeColor={"currentColor"}
className="w-14 h-14"
color="var(--text-heading-color, #111827)"
title={item.symbolIcon.__icon_query__}
/>
url={item.symbolIcon.__icon_url__}
strokeColor={"currentColor"}
className="w-14 h-14"
color="var(--text-heading-color, #111827)"
title={item.symbolIcon.__icon_query__}
/>
) : (
<span className="text-[34px] font-semibold" style={{ color: 'var(--primary-accent-color, #1B8C2D)' }}>{item.symbolText}</span>
)}
@ -145,7 +137,7 @@ const ImageSchema = z.object({
</ul>
</div>
</div>
<div className="pt-[28px]">
<h1 className="font-['Playfair Display'] text-[64px] leading-[1.05] tracking-[-0.01em] max-w-[600px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }} dangerouslySetInnerHTML={{ __html: (slideData?.rightTitle || "Disrupting the\nIndustry").replace(/\n/g, "<br/>") }}></h1>
<div className="mt-[24px] inline-block rounded-md px-6 py-4" style={{ backgroundColor: 'var(--secondary-accent-color, #F3F4F6)' }}>
@ -153,18 +145,18 @@ const ImageSchema = z.object({
{slideData?.rightDescription || "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna"}
</p>
</div>
{/* charts removed */}
{/* flowchart removed */}
</div>
</div>
{/* decorative image removed */}
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -6,124 +6,118 @@ import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://via.placeholder.com/900x500").meta({
description: "URL to image",
__image_url__: z.string().url().default("https://via.placeholder.com/900x500").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(200).default("Wide landscape placeholder representing a slide image area").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://via.placeholder.com/60").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("camera landscape placeholder").meta({
description: "Query used to search the icon. Max 3 words",
}),
})
const layoutId = "header-title-card-slide"
const layoutName = "Icon Image Description"
const layoutDescription = "A slide with a top bar, centered title, placeholder icon area, and a colored card with circular icon, heading, and paragraph"
const Schema = z.object({
meta: z.object({
maxWords: z.number().default(56),
}).default({ maxWords: 56 }),
topBar: z.object({
pageNumber: z.string().min(1).max(3).default("3").meta({
description: "Page number text. Max 1 word",
}),
__image_prompt__: z.string().min(10).max(200).default("Wide landscape placeholder representing a slide image area").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://via.placeholder.com/60").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("camera landscape placeholder").meta({
description: "Query used to search the icon. Max 3 words",
}),
})
const layoutId = "header-title-card-slide"
const layoutName = "Icon Image Description"
const layoutDescription = "A slide with a top bar, centered title, placeholder icon area, and a colored card with circular icon, heading, and paragraph"
const Schema = z.object({
meta: z.object({
maxWords: z.number().default(56),
}).default({ maxWords: 56 }),
topBar: z.object({
pageNumber: z.string().min(1).max(3).default("3").meta({
description: "Page number text. Max 1 word",
}),
}).default({
pageNumber: "3",
}),
title: z.string().min(24).max(56).default("Transforming Ideas into\nReality").meta({
description: "Main title split across up to two lines. Max 10 words",
}),
backgroundImage: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1650831432942-aa352df4e9b4?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Background image covering the entire section behind the card",
}).meta({
description: "Full-bleed background image behind card area",
}),
card: z.object({
circleIcon: IconSchema.default({
__icon_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 24 24' fill='%230A8016' aria-hidden='true'><path d='M10 6V7H7C5.895 7 5 7.895 5 9V18C5 19.105 5.895 20 7 20H17C18.105 20 19 19.105 19 18V9C19 7.895 18.105 7 17 7H14V6C14 4.895 13.105 4 12 4H12C10.895 4 10 4.895 10 6ZM12 6C12.552 6 13 6.448 13 7H11C11 6.448 11.448 6 12 6ZM7 9H17V11H7V9Z'/></svg>",
__icon_query__: "badge document icon",
}),
heading: z.string().min(22).max(70).default("Idea Generation and Validation").meta({
description: "Card heading text. Max 10 words",
}),
body: z.string().min(140).max(450).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.").meta({
description: "Card body paragraph. Max 80 words",
}),
image: ImageSchema.default({
__image_url__: "https://via.placeholder.com/1200x600",
__image_prompt__: "Optional supporting image inside card area",
}).meta({
description: "Optional supporting image for the card. Max 30 words",
}),
}).default({
circleIcon: {
__icon_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 24 24' fill='%230A8016' aria-hidden='true'><path d='M10 6V7H7C5.895 7 5 7.895 5 9V18C5 19.105 5.895 20 7 20H17C18.105 20 19 19.105 19 18V9C19 7.895 18.105 7 17 7H14V6C14 4.895 13.105 4 12 4H12C10.895 4 10 4.895 10 6ZM12 6C12.552 6 13 6.448 13 7H11C11 6.448 11.448 6 12 6ZM7 9H17V11H7V9Z'/></svg>",
__icon_query__: "badge document icon",
},
heading: "Idea Generation and Validation",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
image: {
__image_url__: "https://via.placeholder.com/1200x600",
__image_prompt__: "Optional supporting image inside card area",
},
}),
// charts removed
// diagram removed
}).default({
meta: { maxWords: 56 },
topBar: { pageNumber: "3" },
title: "Transforming Ideas into\nReality",
backgroundImage: {
__image_url__: "https://images.unsplash.com/photo-1650831432942-aa352df4e9b4?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Background image covering the entire section behind the card",
pageNumber: "3",
}),
title: z.string().min(24).max(56).default("Transforming Ideas into\nReality").meta({
description: "Main title split across up to two lines. Max 10 words",
}),
backgroundImage: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1650831432942-aa352df4e9b4?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Background image covering the entire section behind the card",
}).meta({
description: "Full-bleed background image behind card area",
}),
card: z.object({
circleIcon: IconSchema.default({
__icon_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 24 24' fill='%230A8016' aria-hidden='true'><path d='M10 6V7H7C5.895 7 5 7.895 5 9V18C5 19.105 5.895 20 7 20H17C18.105 20 19 19.105 19 18V9C19 7.895 18.105 7 17 7H14V6C14 4.895 13.105 4 12 4H12C10.895 4 10 4.895 10 6ZM12 6C12.552 6 13 6.448 13 7H11C11 6.448 11.448 6 12 6ZM7 9H17V11H7V9Z'/></svg>",
__icon_query__: "badge document icon",
}),
heading: z.string().min(22).max(70).default("Idea Generation and Validation").meta({
description: "Card heading text. Max 10 words",
}),
body: z.string().min(140).max(450).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.").meta({
description: "Card body paragraph. Max 80 words",
}),
image: ImageSchema.default({
__image_url__: "https://via.placeholder.com/1200x600",
__image_prompt__: "Optional supporting image inside card area",
}).meta({
description: "Optional supporting image for the card. Max 30 words",
}),
}).default({
circleIcon: {
__icon_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 24 24' fill='%230A8016' aria-hidden='true'><path d='M10 6V7H7C5.895 7 5 7.895 5 9V18C5 19.105 5.895 20 7 20H17C18.105 20 19 19.105 19 18V9C19 7.895 18.105 7 17 7H14V6C14 4.895 13.105 4 12 4H12C10.895 4 10 4.895 10 6ZM12 6C12.552 6 13 6.448 13 7H11C11 6.448 11.448 6 12 6ZM7 9H17V11H7V9Z'/></svg>",
__icon_query__: "badge document icon",
},
card: {
circleIcon: {
__icon_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 24 24' fill='%230A8016' aria-hidden='true'><path d='M10 6V7H7C5.895 7 5 7.895 5 9V18C5 19.105 5.895 20 7 20H17C18.105 20 19 19.105 19 18V9C19 7.895 18.105 7 17 7H14V6C14 4.895 13.105 4 12 4H12C10.895 4 10 4.895 10 6ZM12 6C12.552 6 13 6.448 13 7H11C11 6.448 11.448 6 12 6ZM7 9H17V11H7V9Z'/></svg>",
__icon_query__: "badge document icon",
},
heading: "Idea Generation and Validation",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
image: { __image_url__: "https://via.placeholder.com/1200x600", __image_prompt__: "Optional supporting image inside card area" },
heading: "Idea Generation and Validation",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
image: {
__image_url__: "https://via.placeholder.com/1200x600",
__image_prompt__: "Optional supporting image inside card area",
},
// charts removed
// diagram removed
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden flex flex-col" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="flex items-center justify-between px-10 pt-6">
<div className="flex items-center gap-6">
{ (slideData as any)?.__companyName__ && <div className="text-[18px] leading-none" style={{ fontFamily: "Playfair Display", color: 'var(--text-heading-color, #111827)' }}>{(slideData as any)?.__companyName__ || "Pitchdeck"}</div>}
<div className="h-[3px] w-[230px] rounded-full" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
{/* page number removed */}
</div>
}),
// charts removed
// diagram removed
}).default({
meta: { maxWords: 56 },
topBar: { pageNumber: "3" },
title: "Transforming Ideas into\nReality",
backgroundImage: {
__image_url__: "https://images.unsplash.com/photo-1650831432942-aa352df4e9b4?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Background image covering the entire section behind the card",
},
card: {
circleIcon: {
__icon_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 24 24' fill='%230A8016' aria-hidden='true'><path d='M10 6V7H7C5.895 7 5 7.895 5 9V18C5 19.105 5.895 20 7 20H17C18.105 20 19 19.105 19 18V9C19 7.895 18.105 7 17 7H14V6C14 4.895 13.105 4 12 4H12C10.895 4 10 4.895 10 6ZM12 6C12.552 6 13 6.448 13 7H11C11 6.448 11.448 6 12 6ZM7 9H17V11H7V9Z'/></svg>",
__icon_query__: "badge document icon",
},
heading: "Idea Generation and Validation",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
image: { __image_url__: "https://via.placeholder.com/1200x600", __image_prompt__: "Optional supporting image inside card area" },
},
// charts removed
// diagram removed
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden flex flex-col" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-10 pt-5 pb-6">
<h1 className="text-[64px] leading-[1.05] text-center font-semibold" style={{ fontFamily: "Playfair Display", color: 'var(--text-heading-color, #111827)' }}>
{(slideData?.title || "").split("\n").map((line, idx) => (
@ -134,7 +128,7 @@ const ImageSchema = z.object({
))}
</h1>
</div>
<div className="relative flex-1 flex items-center justify-center">
<img
src={slideData?.backgroundImage?.__image_url__ || ""}
@ -157,7 +151,7 @@ const ImageSchema = z.object({
) : null}
</div>
</div>
<div className="min-w-0">
<div className="text-white text-[28px] leading-[34px] font-semibold" style={{ fontFamily: "Playfair Display", color: 'var(--text-heading-color, #FFFFFF)' }}>
{slideData?.card?.heading}
@ -165,19 +159,19 @@ const ImageSchema = z.object({
<p className="mt-3 text-white/95 text-[16px] leading-[28px]" style={{ fontFamily: "Playfair Display", color: 'var(--text-body-color, #FFFFFF)' }}>
{slideData?.card?.body}
</p>
{/* Chart section removed */}
{/* Diagram removed */}
</div>
</div>
</div>
</div>
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -3,146 +3,136 @@ import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(160).default("Portrait of a professional team member with subtle background, soft light, business attire").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().url().default("https://static.thenounproject.com/png/1137401-200.png").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("photo image placeholder").meta({
description: "Query used to search the icon. Max 5 words",
}),
})
const layoutId = "header-smallbar-title-team-cards-slide"
const layoutName = "Image List With Description"
const layoutDescription = "A slide with a top utility bar, centered title, and a grid of cards with names, roles, and background images."
const Schema = z.object({
utilityBar: z.object({
pageNumber: z.string().min(1).max(2).default("8").meta({
description: "Page number text. 1-2 digits",
}),
decorativeLine: IconSchema.default({
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='2'%3E%3Crect width='220' height='2' fill='%231FA34A'/%3E%3C/svg%3E",
__icon_query__: "green line separator",
}).meta({
description: "Decorative line representation.",
}),
}).default({
pageNumber: "8",
decorativeLine: {
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='2'%3E%3Crect width='220' height='2' fill='%231FA34A'/%3E%3C/svg%3E",
__icon_query__: "green line separator",
},
}),
title: z.string().min(10).max(50).default("Our Professional Team").meta({
description: "Centered main title. Max 5 words",
}),
cards: z.array(z.object({
name: z.string().min(3).max(30).default("Sam Rawlings").meta({
description: "Member name. Up to 3 words.",
}),
role: z.string().min(20).max(50).default("Marketing specialist with brand and growth experience").meta({
description: "Short description under name. Up to 10 words",
}),
photo: ImageSchema,
})).min(1).max(4).default([
{
name: "Sam Rawlings",
role: "Marketing specialist with brand and growth experience",
photo: {
__image_url__: "https://images.pexels.com/photos/1181695/pexels-photo-1181695.jpeg",
__image_prompt__: "Portrait of a professional team member with subtle background, soft light, business attire",
},
},
{
name: "Sam Rawlings",
role: "Marketing specialist with brand and growth experience",
photo: {
__image_url__: "https://images.pexels.com/photos/450214/pexels-photo-450214.jpeg",
__image_prompt__: "Portrait of a professional team member with subtle background, soft light, business attire",
},
},
{
name: "Sam Rawlings",
role: "Marketing specialist with brand and growth experience",
photo: {
__image_url__: "https://images.pexels.com/photos/756484/pexels-photo-756484.jpeg",
__image_prompt__: "Portrait of a professional team member with subtle background, soft light, business attire",
},
},
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(160).default("Portrait of a professional team member with subtle background, soft light, business attire").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
]).meta({
description: "Grid of member cards with name, role, and image. Up to 4 items",
const IconSchema = z.object({
__icon_url__: z.string().url().default("https://static.thenounproject.com/png/1137401-200.png").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("photo image placeholder").meta({
description: "Query used to search the icon. Max 5 words",
}),
})
const layoutId = "header-smallbar-title-team-cards-slide"
const layoutName = "Image List With Description"
const layoutDescription = "A slide with a top utility bar, centered title, and a grid of cards with names, roles, and background images."
const Schema = z.object({
utilityBar: z.object({
pageNumber: z.string().min(1).max(2).default("8").meta({
description: "Page number text. 1-2 digits",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-10 pt-6 flex items-center justify-between">
<div className="flex items-center">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || "Pitchdeck"}
</span>}
<svg className="ml-6" width="220" height="2" viewBox="0 0 220 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="220" height="2" style={{ fill: 'var(--text-heading-color, #111827)' }}></rect>
</svg>
</div>
{/* page number removed */}
</div>
decorativeLine: IconSchema.default({
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='2'%3E%3Crect width='220' height='2' fill='%231FA34A'/%3E%3C/svg%3E",
__icon_query__: "green line separator",
}).meta({
description: "Decorative line representation.",
}),
}).default({
pageNumber: "8",
decorativeLine: {
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='2'%3E%3Crect width='220' height='2' fill='%231FA34A'/%3E%3C/svg%3E",
__icon_query__: "green line separator",
},
}),
title: z.string().min(10).max(50).default("Our Professional Team").meta({
description: "Centered main title. Max 5 words",
}),
cards: z.array(z.object({
name: z.string().min(3).max(30).default("Sam Rawlings").meta({
description: "Member name. Up to 3 words.",
}),
role: z.string().min(20).max(50).default("Marketing specialist with brand and growth experience").meta({
description: "Short description under name. Up to 10 words",
}),
photo: ImageSchema,
})).min(1).max(4).default([
{
name: "Sam Rawlings",
role: "Marketing specialist with brand and growth experience",
photo: {
__image_url__: "https://images.pexels.com/photos/1181695/pexels-photo-1181695.jpeg",
__image_prompt__: "Portrait of a professional team member with subtle background, soft light, business attire",
},
},
{
name: "Sam Rawlings",
role: "Marketing specialist with brand and growth experience",
photo: {
__image_url__: "https://images.pexels.com/photos/450214/pexels-photo-450214.jpeg",
__image_prompt__: "Portrait of a professional team member with subtle background, soft light, business attire",
},
},
{
name: "Sam Rawlings",
role: "Marketing specialist with brand and growth experience",
photo: {
__image_url__: "https://images.pexels.com/photos/756484/pexels-photo-756484.jpeg",
__image_prompt__: "Portrait of a professional team member with subtle background, soft light, business attire",
},
},
]).meta({
description: "Grid of member cards with name, role, and image. Up to 4 items",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<h1 className="mt-4 text-center text-[64px] leading-[1.05] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{slideData?.title || "Our Professional Team"}
</h1>
<div className="px-10 mt-8">
<div className="mx-auto w-fit">
<div className="grid grid-flow-col auto-cols-[280px] gap-8">
{cards.map((card, idx) => (
<div key={idx} className="w-[280px] flex flex-col rounded-md border border-transparent shadow-[0_12px_36px_rgba(0,0,0,0.08)] overflow-hidden" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-8 pt-10 pb-6 text-center">
<div className="text-[24px] leading-tight " style={{ color: 'var(--text-heading-color, #111827)' }}>
{card.name}
</div>
<div className="mt-3 text-[14px] leading-[22px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{card.role}
</div>
</div>
<div className="relative flex-1 min-h-[300px]" style={{ backgroundColor: 'var(--tertiary-accent-color, #E5E7EB)' }}>
<img src={card.photo.__image_url__} alt={card.photo.__image_prompt__} className="absolute inset-0 w-full h-full object-cover" />
</div>
<div className="px-8 pt-10 pb-6 text-center">
<div className="text-[24px] leading-tight " style={{ color: 'var(--text-heading-color, #111827)' }}>
{card.name}
</div>
<div className="mt-3 text-[14px] leading-[22px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{card.role}
</div>
</div>
<div className="relative flex-1 min-h-[300px]" style={{ backgroundColor: 'var(--tertiary-accent-color, #E5E7EB)' }}>
<img src={card.photo.__image_url__} alt={card.photo.__image_prompt__} className="absolute inset-0 w-full h-full object-cover" />
</div>
</div>
))}
</div>
</div>
</div>
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -2,21 +2,21 @@ import React from 'react'
import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(150).default("High-quality illustrative image for the left panel of a pitch deck cover").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(150).default("High-quality illustrative image for the left panel of a pitch deck cover").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://static.thenounproject.com/png/5563447-200.png").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(40).default("image placeholder icon").meta({
description: "Query used to search the icon. Max 3 words",
}),
__icon_url__: z.string().default("https://static.thenounproject.com/png/5563447-200.png").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(40).default("image placeholder icon").meta({
description: "Query used to search the icon. Max 3 words",
}),
})
const layoutId = "header-counter-two-column-image-text-slide"
@ -24,136 +24,128 @@ const layoutName = "Intro Slide"
const layoutDescription = "A slide with a header row containing label, separator, and counter, followed by a two-column layout with a media area and stacked text blocks. If used as the endig slide then it shoudn't have the intro card."
const Schema = z.object({
header: z.object({
separatorIcon: IconSchema.default({
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='230' height='2' viewBox='0 0 230 2'%3E%3Crect width='230' height='2' fill='%2322863A'/%3E%3C/svg%3E",
__icon_query__: "green line",
}).meta({
description: "Graphic separator element",
}),
counter: z.string().min(1).max(3).default("1").meta({
description: "Small counter text. Max 1 word",
}),
}).default({
separatorIcon: {
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='230' height='2' viewBox='0 0 230 2'%3E%3Crect width='230' height='2' fill='%2322863A'/%3E%3C/svg%3E",
__icon_query__: "green line",
},
counter: "1",
}),
header: z.object({
media: z.object({
type: z.enum(["image"]).default("image").meta({
description: "Choose media type for left panel",
separatorIcon: IconSchema.default({
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='230' height='2' viewBox='0 0 230 2'%3E%3Crect width='230' height='2' fill='%2322863A'/%3E%3C/svg%3E",
__icon_query__: "green line",
}).meta({
description: "Graphic separator element",
}),
counter: z.string().min(1).max(3).default("1").meta({
description: "Small counter text. Max 1 word",
}),
}).default({
separatorIcon: {
__icon_url__: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='230' height='2' viewBox='0 0 230 2'%3E%3Crect width='230' height='2' fill='%2322863A'/%3E%3C/svg%3E",
__icon_query__: "green line",
},
counter: "1",
}),
image: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1557426272-fc759fdf7a8d?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Abstract gradient image suitable for a presentation left panel",
media: z.object({
type: z.enum(["image"]).default("image").meta({
description: "Choose media type for left panel",
}),
image: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1557426272-fc759fdf7a8d?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Abstract gradient image suitable for a presentation left panel",
}),
}).default({
type: "image",
image: {
__image_url__: "https://images.unsplash.com/photo-1557426272-fc759fdf7a8d?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Abstract gradient image suitable for a presentation left panel",
},
}),
}).default({
type: "image",
image: {
__image_url__: "https://images.unsplash.com/photo-1557426272-fc759fdf7a8d?q=80&w=1170&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Abstract gradient image suitable for a presentation left panel",
},
}),
title: z.string().min(12).max(30).default("Introduction Our Pitchdeck").meta({
description: "Main title, supports a line break. Max 6 words",
}),
titleBreakAfter: z.number().min(1).max(25).default(12).meta({
description: "Character index to insert a line break in title",
}),
title: z.string().min(12).max(30).default("Introduction Our Pitchdeck").meta({
description: "Main title, supports a line break. Max 6 words",
}),
titleBreakAfter: z.number().min(1).max(25).default(12).meta({
description: "Character index to insert a line break in title",
}),
paragraph: z.string().min(50).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris").meta({
description: "Paragraph text block. Max 20 words",
}),
paragraph: z.string().min(50).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris").meta({
description: "Paragraph text block. Max 20 words",
}),
introCard: z.object({
enabled: z.boolean().default(false).meta({ description: "Show intro card with name and date" }),
initials: z.string().min(2).max(3).default("PDT").meta({ description: "Initials inside the circle" }),
name: z.string().min(3).max(40).default("Pitch Deck Team").meta({ description: "Display name" }),
date: z.string().min(6).max(40).default("December 22, 2025").meta({ description: "Display date string" }),
}).default({
enabled: true,
initials: "PDT",
name: "Pitch Deck Team",
date: "December 22, 2025",
}),
introCard: z.object({
enabled: z.boolean().default(false).meta({ description: "Show intro card with name and date" }),
initials: z.string().min(2).max(3).default("PDT").meta({ description: "Initials inside the circle" }),
name: z.string().min(3).max(40).default("Pitch Deck Team").meta({ description: "Display name" }),
date: z.string().min(6).max(40).default("December 22, 2025").meta({ description: "Display date string" }),
}).default({
enabled: true,
initials: "PDT",
name: "Pitch Deck Team",
date: "December 22, 2025",
}),
}).meta({
maxWords: 460,
maxWords: 460,
})
type SlideData = z.infer<typeof Schema>
interface LayoutProps {
data?: Partial<SlideData>
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<LayoutProps> = ({ data: slideData }) => {
const title = slideData?.title || "Introduction Our Pitchdeck"
const brIndex = typeof slideData?.titleBreakAfter === "number" ? slideData?.titleBreakAfter as number : 12
const titleFirst = title.slice(0, brIndex)
const titleSecond = title.slice(brIndex)
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="w-full flex items-center justify-between px-10 pt-6">
<div className="flex items-center gap-5">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>{(slideData as any)?.__companyName__ || "Pitchdeck"}</span>}
<svg className="w-[220px] h-[2px]" viewBox="0 0 220 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="220" height="2" style={{ fill: 'var(--text-heading-color, #111827)' }}></rect>
</svg>
</div>
{/* page number removed */}
</div>
const title = slideData?.title || "Introduction Our Pitchdeck"
const brIndex = typeof slideData?.titleBreakAfter === "number" ? slideData?.titleBreakAfter as number : 12
const titleFirst = title.slice(0, brIndex)
const titleSecond = title.slice(brIndex)
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="grid grid-cols-2 h-[calc(100%-64px)]">
<div className="relative h-full overflow-hidden" style={{ backgroundColor: 'var(--tertiary-accent-color, #E5E7EB)' }}>
{slideData?.media?.type === "image" ? (
<img
src={slideData?.media?.image?.__image_url__ || ""}
alt={slideData?.media?.image?.__image_prompt__ || "left media"}
className="absolute inset-0 w-full h-full object-cover"
/>
) : null }
</div>
<div className="h-full" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-14 pt-16 max-w-[640px]">
<h1 className=" text-[64px] leading-[1.06] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{titleFirst}
<br />
{titleSecond}
</h1>
<div className="grid grid-cols-2 h-[calc(100%-64px)]">
<div className="relative h-full overflow-hidden" style={{ backgroundColor: 'var(--tertiary-accent-color, #E5E7EB)' }}>
{slideData?.media?.type === "image" ? (
<img
src={slideData?.media?.image?.__image_url__ || ""}
alt={slideData?.media?.image?.__image_prompt__ || "left media"}
className="absolute inset-0 w-full h-full object-cover"
/>
) : null}
</div>
<p className="mt-8 text-[16px] leading-[28px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{slideData?.paragraph || "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris"}
</p>
<div className="h-full" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-14 pt-16 max-w-[640px]">
<h1 className=" text-[64px] leading-[1.06] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{titleFirst}
<br />
{titleSecond}
</h1>
{slideData?.introCard?.enabled ? (
<div className="mt-10 inline-flex items-center gap-4 border px-5 py-3 shadow-[0_10px_24px_rgba(0,0,0,0.08)] min-w-[400px]" style={{backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="w-16 h-16 rounded-full flex items-center justify-center" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)' }}>
<span className="text-white text-[22px] font-bold tracking-wide" style={{ color: 'var(--text-heading-color, #FFFFFF)' }}>{slideData?.introCard?.initials}</span>
<p className="mt-8 text-[16px] leading-[28px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{slideData?.paragraph || "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris"}
</p>
{slideData?.introCard?.enabled ? (
<div className="mt-10 inline-flex items-center gap-4 border px-5 py-3 shadow-[0_10px_24px_rgba(0,0,0,0.08)] min-w-[400px]" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="w-16 h-16 rounded-full flex items-center justify-center" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)' }}>
<span className="text-white text-[22px] font-bold tracking-wide" style={{ color: 'var(--text-heading-color, #FFFFFF)' }}>{slideData?.introCard?.initials}</span>
</div>
<div className="leading-tight">
<div className="text-[22px] font-semibold" style={{ fontFamily: 'Playfair Display', color: 'var(--text-heading-color, #111827)' }}>{slideData?.introCard?.name}</div>
<div className="text-[15px]" style={{ fontFamily: 'Playfair Display', color: 'var(--text-body-color, #1B8C2D)' }}>{slideData?.introCard?.date}</div>
</div>
</div>
) : null}
</div>
</div>
</div>
<div className="leading-tight">
<div className="text-[22px] font-semibold" style={{ fontFamily: 'Playfair Display', color: 'var(--text-heading-color, #111827)' }}>{slideData?.introCard?.name}</div>
<div className="text-[15px]" style={{ fontFamily: 'Playfair Display', color: 'var(--text-body-color, #1B8C2D)' }}>{slideData?.introCard?.date}</div>
</div>
</div>
) : null}
</div>
</div>
</div>
</div>
</>
)
</div>
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }

View file

@ -3,149 +3,141 @@ import React from 'react'
import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.unsplash.com/photo-1503264116251-35a269479413?q=80&w=1200&auto=format&fit=crop").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(200).default("Elegant abstract green themed background for a presentation slide, minimal shapes, soft lighting").meta({
description: "Prompt used to generate the image. Max 40 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://static.thenounproject.com/png/1783767-200.png").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(40).default("leaf growth").meta({
description: "Query used to search the icon. Max 6 words",
}),
})
const layoutId = "header-tagline-cards-grid-slide"
const layoutName = "Metrics Description"
const layoutDescription = "A slide with a top utility row, a header, a tagline, and a grid of cards each with a number block and text"
const CardSchema = z.object({
number: z.string().min(1).max(5).default("45").meta({
description: "Main number text inside number block. 1 to 3 digits",
}),
numberSymbol: z.string().min(0).max(3).default("%").meta({
description: "Optional symbol next to the number. Single character",
}),
subtitle: z.string().min(8).max(28).default("Subtitle Here").meta({
description: "Card subtitle. Max 5 words",
}),
body: z.string().min(20).max(100).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.").meta({
description: "Card body text. Max 100 characters",
}),
icon: IconSchema.default({
__icon_url__: "https://static.thenounproject.com/png/1783767-200.png",
__icon_query__: "progress indicator",
}).meta({
description: "Optional icon for the card header area",
}),
})
const Schema = z.object({
title: z.string().min(12).max(70).default("Scaling New Heights Together").meta({
description: "Main title. Single line up to ~34 chars or two lines up to ~70 chars. Max 9 words",
}),
tagline: z.string().min(40).max(120).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna").meta({
description: "Subtitle/tagline under title. Max 20 words",
}),
decorativeLine: ImageSchema.default({
__image_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='2' viewBox='0 0 220 2'><rect width='220' height='2' rx='1' fill='%230B8E26'/></svg>",
__image_prompt__: "Thin green horizontal line divider, 220x2, rounded ends",
}).meta({
description: "SVG decorative line asset",
}),
cards: z.array(CardSchema).min(1).max(6).default([
{
number: "87",
numberSymbol: "%",
subtitle: "Customer Satisfaction",
body: "Our customers consistently rate their experience with our products and services as excellent.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "happy customer icon" },
},
{
number: "2.5",
numberSymbol: "M",
subtitle: "Active Users Monthly",
body: "Growing user base actively engaging with our platform across multiple regions worldwide.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "users group icon" },
},
{
number: "99",
numberSymbol: "%",
subtitle: "System Uptime",
body: "Maintaining exceptional reliability with industry-leading system availability and performance.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "server uptime icon" },
},
{
number: "142",
numberSymbol: "+",
subtitle: "Global Partners",
body: "Strategic partnerships driving innovation and market expansion across key industry sectors.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "handshake deal icon" },
},
{
number: "32",
numberSymbol: "x",
subtitle: "Revenue Growth",
body: "Year-over-year growth demonstrating strong market position and business model scalability.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "growth chart icon" },
},
{
number: "500",
numberSymbol: "K",
subtitle: "Carbon Offset",
body: "Committed to sustainability through significant carbon reduction and environmental initiatives.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "leaf sustainability icon" },
},
]).meta({
description: "Grid of cards with number block, subtitle, and body (<=100 chars)",
}),
// chart and diagram removed
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
__image_url__: z.string().url().default("https://images.unsplash.com/photo-1503264116251-35a269479413?q=80&w=1200&auto=format&fit=crop").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(200).default("Elegant abstract green themed background for a presentation slide, minimal shapes, soft lighting").meta({
description: "Prompt used to generate the image. Max 40 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("https://static.thenounproject.com/png/1783767-200.png").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(40).default("leaf growth").meta({
description: "Query used to search the icon. Max 6 words",
}),
})
const layoutId = "header-tagline-cards-grid-slide"
const layoutName = "Metrics Description"
const layoutDescription = "A slide with a top utility row, a header, a tagline, and a grid of cards each with a number block and text"
const CardSchema = z.object({
number: z.string().min(1).max(5).default("45").meta({
description: "Main number text inside number block. 1 to 3 digits",
}),
numberSymbol: z.string().min(0).max(3).default("%").meta({
description: "Optional symbol next to the number. Single character",
}),
subtitle: z.string().min(8).max(28).default("Subtitle Here").meta({
description: "Card subtitle. Max 5 words",
}),
body: z.string().min(20).max(100).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do.").meta({
description: "Card body text. Max 100 characters",
}),
icon: IconSchema.default({
__icon_url__: "https://static.thenounproject.com/png/1783767-200.png",
__icon_query__: "progress indicator",
}).meta({
description: "Optional icon for the card header area",
}),
})
const Schema = z.object({
title: z.string().min(12).max(70).default("Scaling New Heights Together").meta({
description: "Main title. Single line up to ~34 chars or two lines up to ~70 chars. Max 9 words",
}),
tagline: z.string().min(40).max(120).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna").meta({
description: "Subtitle/tagline under title. Max 20 words",
}),
decorativeLine: ImageSchema.default({
__image_url__: "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='2' viewBox='0 0 220 2'><rect width='220' height='2' rx='1' fill='%230B8E26'/></svg>",
__image_prompt__: "Thin green horizontal line divider, 220x2, rounded ends",
}).meta({
description: "SVG decorative line asset",
}),
cards: z.array(CardSchema).min(1).max(6).default([
{
number: "87",
numberSymbol: "%",
subtitle: "Customer Satisfaction",
body: "Our customers consistently rate their experience with our products and services as excellent.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "happy customer icon" },
},
{
number: "2.5",
numberSymbol: "M",
subtitle: "Active Users Monthly",
body: "Growing user base actively engaging with our platform across multiple regions worldwide.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "users group icon" },
},
{
number: "99",
numberSymbol: "%",
subtitle: "System Uptime",
body: "Maintaining exceptional reliability with industry-leading system availability and performance.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "server uptime icon" },
},
{
number: "142",
numberSymbol: "+",
subtitle: "Global Partners",
body: "Strategic partnerships driving innovation and market expansion across key industry sectors.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "handshake deal icon" },
},
{
number: "32",
numberSymbol: "x",
subtitle: "Revenue Growth",
body: "Year-over-year growth demonstrating strong market position and business model scalability.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "growth chart icon" },
},
{
number: "500",
numberSymbol: "K",
subtitle: "Carbon Offset",
body: "Committed to sustainability through significant carbon reduction and environmental initiatives.",
icon: { __icon_url__: "https://static.thenounproject.com/png/1783767-200.png", __icon_query__: "leaf sustainability icon" },
},
]).meta({
description: "Grid of cards with number block, subtitle, and body (<=100 chars)",
}),
// chart and diagram removed
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="h-full flex flex-col px-10 pt-6 pb-8">
<div className="flex items-center justify-between">
<div className="flex items-center gap-4">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] " style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || "Pitchdeck"}
</span>}
<div className="block w-[220px] h-[2px]" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
{/* page number removed */}
</div>
<h1 className="mt-4 text-[64px] leading-[1.06] tracking-tight font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{slideData?.title || "Scaling New Heights Together"}
</h1>
<p className="mt-3 text-[16px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{slideData?.tagline || "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna"}
</p>
<div className="mt-8 grid grid-cols-2 gap-x-10 gap-y-6">
{cards.map((card, idx) => (
<div key={idx} className="rounded-md shadow-sm px-5 py-4" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)' , color: 'var(--card-background-color, #FFFFFF)' }}>
<div key={idx} className="rounded-md shadow-sm px-5 py-4" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)', color: 'var(--card-background-color, #FFFFFF)' }}>
<div className="flex items-start gap-4">
<div className="flex items-baseline shrink-0">
<span className="text-white text-[48px] leading-none" style={{ color: 'var(--text-heading-color, #FFFFFF)' }}>
@ -167,13 +159,13 @@ const ImageSchema = z.object({
</div>
))}
</div>
{/* chart and flowchart removed */}
</div>
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -3,129 +3,119 @@ import React from 'react'
import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(90).default("Professional business meeting scene for roadmap presentation image").meta({
description: "Prompt used to generate the image. Max 18 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("").meta({
description: "Query used to search the icon. Max 6 words",
}),
})
const layoutId = "header-bullets-image-split-slide"
const layoutName = "Numbered Bullet Single Image"
const layoutDescription = "A slide with a top bar, left column with numbered bullets, middle image panel, and right stacked heading with paragraph"
const Schema = z.object({
metaMaxWords: z.number().default(18).meta({
description: "Maximum number of words allowed in any prompt/description metadata fields.",
}),
topBar: z.object({
lineIcon: IconSchema.default({
__icon_url__: "",
__icon_query__: "thin green line",
}).meta({
description: "Decorative line representation with query only",
}),
}).default({
lineIcon: {
__icon_url__: "",
__icon_query__: "thin green line",
},
}),
leftBullets: z.array(
z.object({
numberText: z.string().min(2).max(2).default("01").meta({
description: "Two-digit bullet number. Max 2 chars",
}),
title: z.string().min(10).max(36).default("Strategic Execution").meta({
description: "Bullet title text. Designed for 24px. Max ~36 chars",
}),
body: z.string().min(60).max(100).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.").meta({
description: "Bullet body text. Max ~100 chars",
}),
})
).min(1).max(4).default([
{
numberText: "01",
title: "Strategic Execution",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
},
{
numberText: "02",
title: "Building a Strong Team",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
},
{
numberText: "03",
title: "Market Expansion Strategy",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
},
{
numberText: "04",
title: "Innovation Pipeline",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
}
]).meta({
description: "List of numbered bullets. Max 5 items",
}),
middleImage: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1515623959088-7617915baa1e?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Professional business meeting scene for roadmap presentation image",
__image_url__: z.string().url().default("https://images.pexels.com/photos/31527637/pexels-photo-31527637.jpeg").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(90).default("Professional business meeting scene for roadmap presentation image").meta({
description: "Prompt used to generate the image. Max 18 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().default("").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("").meta({
description: "Query used to search the icon. Max 6 words",
}),
})
const layoutId = "header-bullets-image-split-slide"
const layoutName = "Numbered Bullet Single Image"
const layoutDescription = "A slide with a top bar, left column with numbered bullets, middle image panel, and right stacked heading with paragraph"
const Schema = z.object({
metaMaxWords: z.number().default(18).meta({
description: "Maximum number of words allowed in any prompt/description metadata fields.",
}),
topBar: z.object({
lineIcon: IconSchema.default({
__icon_url__: "",
__icon_query__: "thin green line",
}).meta({
description: "Image displayed in the middle column",
description: "Decorative line representation with query only",
}),
rightHeader: z.object({
heading: z.string().min(6).max(30).default("Our Journey").meta({
description: "Right column heading. Max ~30 chars",
}).default({
lineIcon: {
__icon_url__: "",
__icon_query__: "thin green line",
},
}),
leftBullets: z.array(
z.object({
numberText: z.string().min(2).max(2).default("01").meta({
description: "Two-digit bullet number. Max 2 chars",
}),
paragraph: z.string().min(80).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.").meta({
description: "Right paragraph text. Max 200 chars",
title: z.string().min(10).max(36).default("Strategic Execution").meta({
description: "Bullet title text. Designed for 24px. Max ~36 chars",
}),
}).default({
heading: "Our Journey",
paragraph: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
body: z.string().min(60).max(100).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.").meta({
description: "Bullet body text. Max ~100 chars",
}),
})
).min(1).max(4).default([
{
numberText: "01",
title: "Strategic Execution",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
},
{
numberText: "02",
title: "Building a Strong Team",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
},
{
numberText: "03",
title: "Market Expansion Strategy",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
},
{
numberText: "04",
title: "Innovation Pipeline",
body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor.",
}
]).meta({
description: "List of numbered bullets. Max 5 items",
}),
middleImage: ImageSchema.default({
__image_url__: "https://images.unsplash.com/photo-1515623959088-7617915baa1e?q=80&w=687&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
__image_prompt__: "Professional business meeting scene for roadmap presentation image",
}).meta({
description: "Image displayed in the middle column",
}),
rightHeader: z.object({
heading: z.string().min(6).max(30).default("Our Journey").meta({
description: "Right column heading. Max ~30 chars",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const bullets = slideData?.leftBullets || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="flex items-center justify-between px-10 pt-6">
<div className="flex items-center gap-4">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
{(slideData as any)?.__companyName__ || "Pitchdeck"}
</span>}
<svg className="shrink-0" width="220" height="2" viewBox="0 0 220 2" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 1H220" stroke="var(--text-heading-color, #111827)" strokeWidth="2" />
</svg>
</div>
{/* page number removed */}
</div>
paragraph: z.string().min(80).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.").meta({
description: "Right paragraph text. Max 200 chars",
}),
}).default({
heading: "Our Journey",
paragraph: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore.",
}),
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
}
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const bullets = slideData?.leftBullets || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="grid grid-cols-[34%_33%_33%] h-[calc(100%-64px)] mt-1">
<div className="pl-10 pr-5 pt-8">
<ul className="flex flex-col gap-5">
@ -139,14 +129,14 @@ const ImageSchema = z.object({
{b.body}
</p>
</div>
<div className="w-12 h-12 rounded-full text-white flex items-center justify-center text-[16px] shadow-[0_12px_30px_rgba(0,0,0,0.12)]" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)' , color: 'var(--text-body-color, #FFFFFF)' }}>
<div className="w-12 h-12 rounded-full text-white flex items-center justify-center text-[16px] shadow-[0_12px_30px_rgba(0,0,0,0.12)]" style={{ backgroundColor: 'var(--primary-accent-color, #1B8C2D)', color: 'var(--text-body-color, #FFFFFF)' }}>
{b.numberText}
</div>
</li>
))}
</ul>
</div>
<div className="relative" style={{ backgroundColor: 'var(--tertiary-accent-color, #E5E7EB)' }}>
<img
src={slideData?.middleImage?.__image_url__ || ""}
@ -155,7 +145,7 @@ const ImageSchema = z.object({
/>
{/* overlay removed */}
</div>
<div className="pl-10 pr-12 pt-16">
<div className="max-w-[560px] mx-auto">
<h1 className="text-[64px] leading-[0.95] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>
@ -168,9 +158,9 @@ const ImageSchema = z.object({
</div>
</div>
</div>
</>
)
}
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout
export { Schema, layoutId, layoutName, layoutDescription }
export default dynamicSlideLayout

View file

@ -2,7 +2,7 @@ import React from "react"
import * as z from "zod"
const layoutId = "table-of-contents-layout"
const layoutName = "Table Of Contents"
const layoutName = "Table Of Contents"
const layoutDescription = "Header with brand marker, title, optional description, and a two-column table of contents list"
const ToCItemSchema = z
@ -19,12 +19,12 @@ const Schema = z
.object({
topBar: z
.object({
marker: z.string().min(1).max(3).default("2").meta({
description: "Numeric marker on the top bar. Up to 3 digits",
}),
})
.default({ marker: "2" }),
.default({ marker: "2" }),
title: z
.string()
@ -61,7 +61,7 @@ const Schema = z
.meta({ description: "List of contents (3-10)" }),
})
.default({
topBar: { marker: "2" },
topBar: { marker: "2" },
title: "Table Of Contents",
description:
"Use this as a quick guide to navigate the presentation sections.",
@ -102,23 +102,7 @@ const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) =>
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
<div className="px-12 pt-6 pb-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-6">
{ (slideData as any)?.__companyName__ && <span
className="text-[18px] font-semibold "
style={{ color: "var(--primary-accent-color, #1B8C2D)" }}
>
{(slideData as any)?.__companyName__ || "Pitchdeck"}
</span>}
<div
className="h-[2px] w-[220px]"
style={{ backgroundColor: "var(--primary-accent-color, #1B8C2D)" }}
></div>
</div>
{/* page number intentionally omitted */}
</div>
</div>
<div className="px-12">
<h1

View file

@ -4,187 +4,179 @@ import * as z from "zod";
const ImageSchema = z.object({
__image_url__: z.string().url().default("https://images.unsplash.com/photo-1522199755839-a2bacb67c546?q=80&w=1200&auto=format&fit=crop").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(150).default("Abstract light background for slide header area").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
__image_url__: z.string().url().default("https://images.unsplash.com/photo-1522199755839-a2bacb67c546?q=80&w=1200&auto=format&fit=crop").meta({
description: "URL to image",
}),
__image_prompt__: z.string().min(10).max(150).default("Abstract light background for slide header area").meta({
description: "Prompt used to generate the image. Max 30 words",
}),
})
const IconSchema = z.object({
__icon_url__: z.string().url().default("https://cdn.jsdelivr.net/gh/tabler/tabler-icons/icons/placeholder.svg").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("progress ring placeholder").meta({
description: "Query used to search the icon. Max 3 words",
}),
__icon_url__: z.string().url().default("https://cdn.jsdelivr.net/gh/tabler/tabler-icons/icons/placeholder.svg").meta({
description: "URL to icon",
}),
__icon_query__: z.string().min(3).max(30).default("progress ring placeholder").meta({
description: "Query used to search the icon. Max 3 words",
}),
})
const layoutId = "visual-metrics"
const layoutId = "visual-metrics"
const layoutName = "Visual Metrics"
const layoutDescription = "A slide with a header bar, numeric marker, title, description, and grid of cards with headings, circular metrics, and texts"
const CardSchema = z.object({
title: z.string().min(6).max(18).default("Research").meta({
description: "Card heading. Max 3 words",
}),
value: z.number().min(0).max(9999).default(67).meta({
description: "Numeric value to display",
}),
unit: z.string().min(0).max(2).default("K").meta({
description: "Unit for the value, e.g., %, K, M",
}),
description: z.string().min(1).max(50).default("Lorem ipsum dolor sit amet, consectetur").meta({
description: "Card supporting text. Max 50 characters",
}),
title: z.string().min(6).max(18).default("Research").meta({
description: "Card heading. Max 3 words",
}),
value: z.number().min(0).max(9999).default(67).meta({
description: "Numeric value to display",
}),
unit: z.string().min(0).max(2).default("K").meta({
description: "Unit for the value, e.g., %, K, M",
}),
description: z.string().min(1).max(50).default("Lorem ipsum dolor sit amet, consectetur").meta({
description: "Card supporting text. Max 50 characters",
}),
}).default({
title: "Research",
value: 67,
unit: "K",
description: "Lorem ipsum dolor sit amet, consectetur",
title: "Research",
value: 67,
unit: "K",
description: "Lorem ipsum dolor sit amet, consectetur",
})
const Schema = z.object({
topBar: z.object({
marker: z.string().min(1).max(3).default("2").meta({
description: "Numeric marker on the top bar. Up to 3 digits",
topBar: z.object({
marker: z.string().min(1).max(3).default("2").meta({
description: "Numeric marker on the top bar. Up to 3 digits",
}),
}).default({
marker: "2",
}),
title: z.string().min(20).max(68).default("Our Vision And Strategy For Excellence").meta({
description: "Main slide title. Max 10 words",
}),
description: z.string().min(70).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation").meta({
description: "Lead paragraph. Max 35 words",
}),
cards: z.array(CardSchema).min(1).max(4).default([
{
title: "Research",
value: 6700,
unit: "K",
description: "Lorem ipsum dolor sit amet, consectetur",
},
{
title: "Development",
value: 80,
unit: "K",
description: "Lorem ipsum dolor sit amet, consectetur",
},
{
title: "Research",
value: 67,
unit: "%",
description: "Lorem ipsum dolor sit amet, consectetur",
},
{ title: "Development", value: 80, unit: "K", description: "Lorem ipsum dolor sit amet, consectetur" },
]).meta({
description: "Grid of cards with heading, circular metric, and text",
}),
chartPalette: z.array(z.string().min(4).max(20)).min(2).max(6).default(["var(--primary-accent-color, #1B8C2D)", "var(--tertiary-accent-color, #E5E7EB)", "#f59e0b", "#3b82f6"]).meta({
description: "Palette for charts",
}),
}).default({
marker: "2",
}),
title: z.string().min(20).max(68).default("Our Vision And Strategy For Excellence").meta({
description: "Main slide title. Max 10 words",
}),
description: z.string().min(70).max(200).default("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation").meta({
description: "Lead paragraph. Max 35 words",
}),
cards: z.array(CardSchema).min(1).max(4).default([
{
title: "Research",
value: 6700,
unit: "K",
description: "Lorem ipsum dolor sit amet, consectetur",
},
{
title: "Development",
value: 80,
unit: "K",
description: "Lorem ipsum dolor sit amet, consectetur",
},
{
title: "Research",
value: 67,
unit: "%",
description: "Lorem ipsum dolor sit amet, consectetur",
},
{ title: "Development", value: 80, unit: "K", description: "Lorem ipsum dolor sit amet, consectetur" },
]).meta({
description: "Grid of cards with heading, circular metric, and text",
}),
chartPalette: z.array(z.string().min(4).max(20)).min(2).max(6).default(["var(--primary-accent-color, #1B8C2D)", "var(--tertiary-accent-color, #E5E7EB)", "#f59e0b", "#3b82f6"]).meta({
description: "Palette for charts",
}),
}).default({
topBar: { marker: "2" },
title: "Our Vision And Strategy For Excellence",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation",
cards: [
{ title: "Research", value: 67, unit: "K", description: "Lorem ipsum dolor sit amet, consectetur" },
{ title: "Ops", value: 42, unit: "M", description: "Lorem ipsum dolor sit amet, consectetur" },
{ title: "Efficiency", value: 67, unit: "%", description: "Lorem ipsum dolor sit amet, consectetur" },
{ title: "Development", value: 80, unit: "K", description: "Lorem ipsum dolor sit amet, consectetur" },
],
chartPalette: ["var(--primary-accent-color, #1B8C2D)", "var(--tertiary-accent-color, #E5E7EB)", "#f59e0b", "#3b82f6"],
topBar: { marker: "2" },
title: "Our Vision And Strategy For Excellence",
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation",
cards: [
{ title: "Research", value: 67, unit: "K", description: "Lorem ipsum dolor sit amet, consectetur" },
{ title: "Ops", value: 42, unit: "M", description: "Lorem ipsum dolor sit amet, consectetur" },
{ title: "Efficiency", value: 67, unit: "%", description: "Lorem ipsum dolor sit amet, consectetur" },
{ title: "Development", value: 80, unit: "K", description: "Lorem ipsum dolor sit amet, consectetur" },
],
chartPalette: ["var(--primary-accent-color, #1B8C2D)", "var(--tertiary-accent-color, #E5E7EB)", "#f59e0b", "#3b82f6"],
})
type SlideData = z.infer<typeof Schema>
interface SlideLayoutProps {
data?: Partial<SlideData>
data?: Partial<SlideData>
}
// removed external chart components; using inline SVG ring only
const dynamicSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily:"var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-12 pt-6 pb-2">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-6">
{ (slideData as any)?.__companyName__ && <span className="text-[18px] font-semibold " style={{ color: 'var(--text-heading-color, #111827)' }}>{(slideData as any)?.__companyName__ || "Pitchdeck"}</span>}
<div className="h-[2px] w-[220px]" style={{ backgroundColor: 'var(--text-heading-color, #111827)' }}></div>
</div>
{/* page number removed */}
</div>
</div>
const cards = slideData?.cards || []
return (
<>
<link
href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<div className=" w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video relative z-20 mx-auto overflow-hidden" style={{ fontFamily: "var(--heading-font-family,Playfair Display)", backgroundColor: 'var(--card-background-color, #FFFFFF)' }}>
<div className="px-12">
<h1 className="text-[64px] leading-[1.05] tracking-tight font-semibold mt-2" style={{ color: 'var(--text-heading-color, #111827)' }}>
{slideData?.title}
</h1>
<p className="mt-5 text-[16px] leading-[1.6] max-w-[1020px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{slideData?.description}
</p>
</div>
<div className="px-10 mt-10">
<div className="grid grid-cols-4 gap-8">
{cards.map((card, idx) => {
const radius = 80
const circumference = 2 * Math.PI * radius
const dasharray = circumference
return (
<div key={idx} className="rounded-xl border shadow-[0_24px_60px_rgba(0,0,0,0.08)]" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)', borderColor: 'rgba(0,0,0,0.06)' }}>
<div className="px-8 pt-8 pb-7 flex flex-col items-center text-center">
<h3 className="text-[24px] leading-tight font-semibold " style={{ color: 'var(--text-heading-color, #111827)' }}>{card.title}</h3>
<div className="mt-6 relative w-[180px] h-[180px]">
{/* donut ring retained */}
<svg viewBox="0 0 200 200" className="w-full h-full">
<circle cx="100" cy="100" r="80" fill="none" stroke="var(--tertiary-accent-color, #E5E7EB)" strokeWidth="16"></circle>
<circle
cx="100"
cy="100"
r="80"
fill="none"
stroke={'var(--primary-accent-color, #1B8C2D)'}
strokeWidth="16"
strokeLinecap="round"
strokeDasharray={dasharray}
strokeDashoffset={0}
transform="rotate(-90 100 100)"
></circle>
<circle cx="100" cy="100" r="62" fill="none" stroke="rgba(0,0,0,0.06)" strokeWidth="10"></circle>
</svg>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-[32px] font-extrabold " style={{ color: 'var(--text-heading-color, #111827)' }}>{card.value}{card.unit}</span>
<div className="px-12">
<h1 className="text-[64px] leading-[1.05] tracking-tight font-semibold mt-2" style={{ color: 'var(--text-heading-color, #111827)' }}>
{slideData?.title}
</h1>
<p className="mt-5 text-[16px] leading-[1.6] max-w-[1020px] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{slideData?.description}
</p>
</div>
<div className="px-10 mt-10">
<div className="grid grid-cols-4 gap-8">
{cards.map((card, idx) => {
const radius = 80
const circumference = 2 * Math.PI * radius
const dasharray = circumference
return (
<div key={idx} className="rounded-xl border shadow-[0_24px_60px_rgba(0,0,0,0.08)]" style={{ backgroundColor: 'var(--card-background-color, #FFFFFF)', borderColor: 'rgba(0,0,0,0.06)' }}>
<div className="px-8 pt-8 pb-7 flex flex-col items-center text-center">
<h3 className="text-[24px] leading-tight font-semibold " style={{ color: 'var(--text-heading-color, #111827)' }}>{card.title}</h3>
<div className="mt-6 relative w-[180px] h-[180px]">
{/* donut ring retained */}
<svg viewBox="0 0 200 200" className="w-full h-full">
<circle cx="100" cy="100" r="80" fill="none" stroke="var(--tertiary-accent-color, #E5E7EB)" strokeWidth="16"></circle>
<circle
cx="100"
cy="100"
r="80"
fill="none"
stroke={'var(--primary-accent-color, #1B8C2D)'}
strokeWidth="16"
strokeLinecap="round"
strokeDasharray={dasharray}
strokeDashoffset={0}
transform="rotate(-90 100 100)"
></circle>
<circle cx="100" cy="100" r="62" fill="none" stroke="rgba(0,0,0,0.06)" strokeWidth="10"></circle>
</svg>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-[32px] font-extrabold " style={{ color: 'var(--text-heading-color, #111827)' }}>{card.value}{card.unit}</span>
</div>
</div>
<p className="mt-6 text-[16px] leading-[1.6] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{card.description}
</p>
</div>
</div>
)
})}
</div>
</div>
<p className="mt-6 text-[16px] leading-[1.6] " style={{ color: 'var(--text-body-color, #6B7280)' }}>
{card.description}
</p>
</div>
</div>
)
})}
</div>
</div>
{/* mermaid removed */}
</div>
</>
)
{/* mermaid removed */}
</div>
</>
)
}
export { Schema, layoutId, layoutName, layoutDescription }

View file

@ -94,7 +94,7 @@ const Schema = z
website: z.string().min(6).max(60).default("www.yourwebsite.com"),
})
.default({
title: "Our Infographic",
title: "Our Infographic",
sideHeading: "Lorem ipsum dolor sit amet,",
sideParagraph:
"Concise paragraph describing context. Keep it short and readable across one or two lines.",
@ -163,15 +163,7 @@ const InfographicFourIcons: React.FC<SlideLayoutProps> = ({ data }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{(slideData as any )?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
</div>
{/* Title + right paragraph */}
<div className="px-12 pt-2">

View file

@ -3,7 +3,7 @@ import * as z from "zod"
import { RemoteSvgIcon } from "@/app/hooks/useRemoteSvgIcon";
const layoutId = "icon-bullet-list-description-slide"
const layoutName = "Icon Bullet List Description"
const layoutName = "Icon Bullet List Description"
const layoutDescription = "Bullet list with title, description, and icon"
const IconSchema = z
@ -39,7 +39,7 @@ const FeatureSchema = z
const Schema = z
.object({
title: z
.string()
.min(8)
@ -86,7 +86,7 @@ const Schema = z
website: z.string().min(6).max(60).default("www.yourwebsite.com"),
})
.default({
title: "Key Product Features",
description:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean commodo ligula eget dolor.",
@ -142,13 +142,8 @@ const FeatureCards: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{(slideData as any)?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
{/* Decorative right image area removed to keep imagery-driven design */}
@ -163,7 +158,7 @@ const FeatureCards: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
{/* Feature cards */}
<div className="relative px-12 mt-8">
<div className="grid grid-flow-col auto-cols-[260px] gap-6 justify-center">
{features.slice(0,4).map((f, i) => (
{features.slice(0, 4).map((f, i) => (
<div key={i} className="rounded-[22px] shadow-[0_16px_40px_rgba(0,0,0,0.08)] overflow-hidden" style={{ backgroundColor: 'var(--primary-accent-color, #BFF4FF)' }}>
<div className="px-6 py-5">
<div className="w-10 h-10 rounded-sm flex items-center justify-center" style={{ backgroundColor: 'var(--secondary-accent-color, #FFFFFF)' }}>

View file

@ -99,21 +99,7 @@ const TeamMembers: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header: diamond + business name */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div
className="w-3 h-3 rotate-45"
style={{ backgroundColor: "var(--text-heading-color, #111827)" }}
></div>
{ (slideData as any)?.__companyName__ && <span
className="text-[16px]"
style={{ color: "var(--text-body-color, #6B7280)" }}
>
{(slideData as any)?.__companyName__}
</span>}
</div>
</div>
<div className="px-12 pt-6 grid grid-cols-[36%_64%] gap-8 items-start">
{/* Left text stack */}

View file

@ -90,21 +90,7 @@ const IntroSlideLayout: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header: diamond + business name */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div
className="w-3 h-3 rotate-45"
style={{ backgroundColor: "var(--text-heading-color, #111827)" }}
></div>
{ (slideData as any)?.__companyName__ && <span
className="text-[16px] "
style={{ color: "var(--text-body-color, #6B7280)" }}
>
{(slideData as any)?.__companyName__}
</span>}
</div>
</div>
{/* Right panel image (replaces dark gradient box) */}
<div className="absolute top-0 right-0 h-[520px] w-[36%] overflow-hidden">

View file

@ -117,13 +117,7 @@ const MetricsNumbers: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{ (slideData as any)?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
{/* Separator line like the reference */}
<div className="absolute top-0 left-1/2 w-[1px] h-full" style={{ backgroundColor: "rgba(0,0,0,0.1)" }}></div>
@ -147,7 +141,7 @@ const MetricsNumbers: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
<div className="absolute top-6 -right-24 w-[220px] h-[220px] rounded-full border" style={{ borderColor: "rgba(0,0,0,0.2)" }}></div>
<div className="flex flex-col gap-6">
{metrics.slice(0,3).map((m, i) => (
{metrics.slice(0, 3).map((m, i) => (
<div key={i} className="rounded-[18px] px-6 py-5 grid grid-cols-[38%_62%] items-start shadow-[0_16px_40px_rgba(0,0,0,0.08)]" style={{ backgroundColor: 'var(--primary-accent-color, #BFF4FF)' }}>
<div className="text-[40px] font-semibold" style={{ color: 'var(--text-heading-color, #111827)' }}>{m.value}</div>
<div>

View file

@ -70,13 +70,7 @@ const CommitmentTwoPoints: React.FC<SlideLayoutProps> = ({ data: slideData }) =>
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{ (slideData as any)?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
{/* Subtle background motif */}
<div

View file

@ -81,13 +81,7 @@ const TableOfContents: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{ (slideData as any)?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
<div className="px-12 pt-3">
<h1 className="text-[48px] leading-[1.1] font-semibold" style={{ color: "var(--text-heading-color, #111827)" }}>{slideData?.title}</h1>

View file

@ -157,13 +157,7 @@ const TableOrChart: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
backgroundColor: "var(--card-background-color, #FFFFFF)",
}}
>
{/* Header */}
<div className="px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{(slideData as any)?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
{/* Title and description */}
<div className="px-12 pt-3">

View file

@ -96,13 +96,7 @@ const Timeline: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
>
{/* Keep white background to match Swift layouts */}
{/* Header: diamond + business name */}
<div className="relative px-12 pt-6 pb-2">
<div className="flex items-center gap-3">
<div className="w-3 h-3 rotate-45" style={{ backgroundColor: "var(--text-heading-color, #111827)" }}></div>
{(slideData as any)?.__companyName__ && <span className="text-[16px]" style={{ color: "var(--text-body-color, #6B7280)" }}>{(slideData as any)?.__companyName__}</span>}
</div>
</div>
{/* Right vertical diamonds */}
<div className="absolute top-16 right-6 flex flex-col items-center gap-3">