Merge branch 'feat/custom_schema_and_layout' of github.com:presenton/presenton into feat/custom_schema_and_layout

This commit is contained in:
sauravniraula 2025-07-16 20:43:28 +05:45
commit ca42467bcf
No known key found for this signature in database
GPG key ID: 60FCC1B5A5E83326
19 changed files with 1125 additions and 163 deletions

View file

@ -1,8 +1,10 @@
import { useEditor, EditorContent } from "@tiptap/react"
import StarterKit from "@tiptap/starter-kit"
import { Markdown } from "tiptap-markdown"
import { useEffect } from "react"
export default function MarkdownEditor({ content, onChange }: { content: string; onChange: (content: string) => void }) {
const editor = useEditor({
extensions: [StarterKit, Markdown],
content: content,
@ -18,6 +20,13 @@ export default function MarkdownEditor({ content, onChange }: { content: string;
immediatelyRender: false,
});
// Update editor content when the content prop changes (for streaming)
useEffect(() => {
if (editor && content !== editor.storage.markdown.getMarkdown()) {
editor.commands.setContent(content);
}
}, [content, editor]);
return (
<div className="relative">

View file

@ -1,7 +1,6 @@
import { useState, useEffect } from "react";
import { toast } from "@/hooks/use-toast";
import { zodToJsonSchema } from "zod-to-json-schema";
import * as z from 'zod';
interface LayoutInfo {
id: string;
name?: string;
@ -17,6 +16,8 @@ interface LayoutInfo {
const useLayoutSchema = () => {
const [layoutSchema, setLayoutSchema] = useState<LayoutInfo[] | null>(null);
const [idMapFileNames, setIdMapFileNames] = useState<Record<string, string>>({});
const [idMapSchema, setIdMapSchema] = useState<Record<string, z.ZodSchema>>({});
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@ -24,11 +25,13 @@ const useLayoutSchema = () => {
try {
setLoading(true);
setError(null);
const response = await fetch('/api/layouts');
const layoutFiles = await response.json();
const layouts = await extractSchema(layoutFiles);
// console.log(layouts);
setLayoutSchema(layouts || []);
const layoutResponse = await fetch('/api/layouts');
const layoutFiles = await layoutResponse.json();
const response = await extractSchema(layoutFiles);
setLayoutSchema(response?.layouts || []);
setIdMapFileNames(response?.idMapFileNames || {});
setIdMapSchema(response?.idMapSchema || {});
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : 'Failed to load layouts';
setError(errorMessage);
@ -38,6 +41,8 @@ const useLayoutSchema = () => {
}
};
// Auto-load layouts on mount
useEffect(() => {
loadLayouts();
@ -48,7 +53,9 @@ const useLayoutSchema = () => {
setLayoutSchema,
loading,
error,
refetch: loadLayouts
refetch: loadLayouts,
idMapFileNames,
idMapSchema
};
};
@ -57,6 +64,8 @@ export default useLayoutSchema;
const extractSchema = async (layoutFiles: string[]) => {
const layouts: LayoutInfo[] = [];
const idMapFileNames: Record<string, string> = {};
const idMapSchema: Record<string, z.ZodSchema> = {};
for (const fileName of layoutFiles) {
try {
const file = fileName.replace('.tsx', '').replace('.ts', '')
@ -88,18 +97,24 @@ const extractSchema = async (layoutFiles: string[]) => {
}
const layoutName = module.layoutName
const layoutDescription = module.layoutDescription
const jsonSchema = zodToJsonSchema(module.Schema)
const jsonSchema = z.toJSONSchema(module.Schema,{
override :(ctx)=>{
delete ctx.jsonSchema.default
},
})
const layout = {
id: layoutId,
name: layoutName,
description: layoutDescription,
json_schema: jsonSchema
json_schema: jsonSchema,
}
idMapFileNames[layoutId] = fileName
idMapSchema[layoutId] = module.Schema
layouts.push(layout)
} catch (error) {
console.error(`Error extracting schema for ${fileName}:`, error)
return null
}
}
return layouts
return {layouts, idMapFileNames, idMapSchema}
};

View file

@ -95,6 +95,7 @@ export function OutlineItem({
{/* Editable Markdown Content */}
<MarkdownEditor
key={index}
content={slideOutline.body || ''}
onChange={(content) => handleSlideChange({ ...slideOutline, body: content })}
/>

View file

@ -73,7 +73,7 @@ const OutlinePage = () => {
);
evtSource.onopen = () => {
console.log('connection open');
};
evtSource.addEventListener("response", (event) => {
@ -85,7 +85,6 @@ const OutlinePage = () => {
try {
const repairedJson = jsonrepair(accumulatedChunks);
const partialData = JSON.parse(repairedJson);
if (partialData.slides) {
dispatch(setOutlines(partialData.slides));
setLoading(false);

View file

@ -147,7 +147,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => {
try {
const repairedJson = jsonrepair(accumulatedChunks);
const partialData = JSON.parse(repairedJson);
console.log(partialData);
if (partialData.slides) {
// Check if the length of slides has changed
if (

View file

@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react";
import { Slide } from "../../types/slide";
import { renderSlideContent } from "../../components/slide_config";
import { Loader2, PlusIcon, Trash2, WandSparkles } from "lucide-react";
import {
Popover,
@ -17,6 +16,9 @@ import { useDispatch, useSelector } from "react-redux";
import { addSlide, updateSlide } from "@/store/slices/presentationGeneration";
import NewSlide from "../../components/slide_layouts/NewSlide";
import { getEmptySlideContent } from "../../utils/NewSlideContent";
import useLayoutSchema from "../../hooks/useLayoutSchema";
import dynamic from "next/dynamic";
import FirstSlideLayout from "@/components/layouts/FirstSlideLayout";
interface SlideContentProps {
slide: Slide;
@ -27,6 +29,7 @@ interface SlideContentProps {
onDeleteSlide: (index: number) => void;
}
const SlideContent = ({
slide,
index,
@ -40,6 +43,7 @@ const SlideContent = ({
const { presentationData, isStreaming } = useSelector(
(state: RootState) => state.presentationGeneration
);
const { idMapFileNames, idMapSchema } = useLayoutSchema();
const handleSubmit = async () => {
const element = document.getElementById(
@ -94,24 +98,29 @@ const SlideContent = ({
setShowNewSlideSelection(false);
};
// Scroll to the new slide when the presentationData is updated
useEffect(() => {
if (
presentationData &&
presentationData?.slides &&
presentationData.slides.length > 1 &&
isStreaming
) {
const slideElement = document.getElementById(`slide-${index}`);
if (slideElement) {
slideElement.scrollIntoView({
behavior: "smooth",
block: "center",
});
}
}
}, [presentationData?.slides, isStreaming]);
// useEffect(() => {
// if (
// presentationData &&
// presentationData?.slides &&
// presentationData.slides.length > 1 &&
// isStreaming
// ) {
// const slideElement = document.getElementById(`slide-${index}`);
// if (slideElement) {
// slideElement.scrollIntoView({
// behavior: "smooth",
// block: "center",
// });
// }
// }
// }, [presentationData?.slides, isStreaming]);
const renderLayout = (slide: any) => {
const layoutName = idMapFileNames[slide.layoutId];
const Layout = dynamic(() => import(`@/components/layouts/${layoutName}.tsx`));
return <Layout data={slide.content} />
};
const language = presentationData?.presentation?.language || "English";
return (
<>
<div
@ -122,7 +131,8 @@ const SlideContent = ({
<Loader2 className="w-8 h-8 absolute right-2 top-2 z-30 text-blue-800 animate-spin" />
)}
<div className={` w-full group `}>
{renderSlideContent(slide, language)}
{/* render slides */}
{renderLayout(slide)}
{!showNewSlideSelection && (
<div className="group-hover:opacity-100 hidden md:block opacity-0 transition-opacity my-4 duration-300">

View file

@ -1,5 +1,4 @@
import React from 'react'
import { zodToJsonSchema } from "zod-to-json-schema";
import * as z from "zod";
export const layoutId = 'bullet-point-slide'
@ -7,29 +6,40 @@ export const layoutName = 'Bullet Point Slide'
export const layoutDescription = 'A slide with a title, subtitle, and a list of bullet points.'
const imageSchema = z.object({
url: z.string().url().describe('URL to image'),
prompt: z.string().describe('Prompt used to generate the image'),
url: z.url().meta({
description: "URL to image",
}),
prompt: z.string().meta({
description: "Prompt used to generate the image",
}),
})
const bulletPointSlideSchema = z.object({
title: z.string().min(3).max(100).default('Key Points').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
icon: z.string().optional().describe('Icon to display in the slide'),
title: z.string().min(3).max(100).default('Key Points').meta({
description: "Title of the slide",
badu: "'badf"
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
icon: z.string().optional().meta({
description: "Icon to display in the slide",
}),
bulletPoints: z.array(z.string().min(5).max(200)).min(2).max(8).default([
'First key point that highlights important information',
'Second bullet point with valuable insights',
'Third point demonstrating clear benefits',
'Fourth item showcasing key features'
]).describe('List of bullet points (2-8 items)'),
backgroundImage: imageSchema.optional().describe('Background image for the slide'),
]).meta({
description: "List of bullet points (2-8 items)",
}),
backgroundImage: imageSchema.optional().meta({
description: "Background image for the slide",
}),
})
export const Schema = bulletPointSlideSchema
console.log(zodToJsonSchema(Schema, {
removeAdditionalStrategy: 'strict',
}))
console.log(z.toJSONSchema(Schema))
export type BulletPointSlideData = z.infer<typeof bulletPointSlideSchema>

View file

@ -6,21 +6,39 @@ export const layoutName = 'Conclusion Slide'
export const layoutDescription = 'A slide with a title, subtitle, key takeaways, call to action, and contact information'
const conclusionSlideSchema = z.object({
title: z.string().min(3).max(100).default('Conclusion').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
title: z.string().min(3).max(100).default('Conclusion').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
keyTakeaways: z.array(z.string().min(5).max(200)).min(2).max(6).default([
'Successfully achieved our primary objectives',
'Demonstrated significant value and impact',
'Established clear next steps for continued success',
'Built strong foundation for future growth'
]).describe('Key takeaways or summary points (2-6 items)'),
callToAction: z.string().min(5).max(150).optional().describe('Optional call to action or next steps'),
]).meta({
description: "Key takeaways or summary points (2-6 items)",
}),
callToAction: z.string().min(5).max(150).optional().meta({
description: "Optional call to action or next steps",
}),
contactInfo: z.object({
email: z.string().email().optional().describe('Contact email'),
phone: z.string().min(5).max(50).optional().describe('Contact phone number'),
website: z.string().url().optional().describe('Website URL')
}).optional().describe('Optional contact information'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
email: z.string().email().optional().meta({
description: "Contact email",
}),
phone: z.string().min(5).max(50).optional().meta({
description: "Contact phone number",
}),
website: z.string().url().optional().meta({
description: "Website URL",
})
}).optional().meta({
description: "Optional contact information",
}),
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = conclusionSlideSchema

View file

@ -7,10 +7,18 @@ export const layoutName = 'Content Slide'
export const layoutDescription = 'A slide with a title, subtitle, and content'
const contentSlideSchema = z.object({
title: z.string().min(3).max(100).default('Slide Title').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
content: z.string().min(10).max(1000).default('Your slide content goes here. This is where you can add detailed information, explanations, or any other text content that supports your presentation.').describe('Main content text'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
title: z.string().min(3).max(100).default('Slide Title').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
content: z.string().min(10).max(1000).default('Your slide content goes here. This is where you can add detailed information, explanations, or any other text content that supports your presentation.').meta({
description: "Main content text",
}),
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = contentSlideSchema

View file

@ -7,12 +7,24 @@ export const layoutName = 'First Slide'
export const layoutDescription = 'A slide with a title, subtitle, author, date, company, and background image'
const firstSlideSchema = z.object({
title: z.string().min(3).max(100).default('Welcome to Our Presentation').describe('Main title of the presentation'),
subtitle: z.string().min(10).max(200).default('Subtitle for the slide').optional().describe('Optional subtitle or tagline'),
author: z.string().min(2).max(100).default('John Doe').optional().describe('Author or presenter name'),
date: z.string().optional().describe('Presentation date'),
company: z.string().min(2).max(100).default('Company Name').optional().describe('Company or organization name'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
title: z.string().min(3).max(100).default('Welcome to Our Presentation').meta({
description: "Main title of the presentation",
}),
subtitle: z.string().min(10).max(200).default('Subtitle for the slide').optional().meta({
description: "Optional subtitle or tagline",
}),
author: z.string().min(2).max(100).default('John Doe').optional().meta({
description: "Author or presenter name",
}),
date: z.string().optional().meta({
description: "Presentation date",
}),
company: z.string().min(2).max(100).default('Company Name').optional().meta({
description: "Company or organization name",
}),
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = firstSlideSchema

View file

@ -7,12 +7,24 @@ export const layoutName = 'Image Slide'
export const layoutDescription = 'A slide with a title, subtitle, image, and content'
const imageSlideSchema = z.object({
title: z.string().min(3).max(100).default('Image Showcase').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).default('Subtitle for the slide').optional().describe('Optional subtitle or description'),
image: z.string().default('https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800&h=600&fit=crop').describe('Main image URL'),
imageCaption: z.string().min(5).max(200).default('Image caption').optional().describe('Optional image caption or description'),
content: z.string().min(10).max(600).optional().describe('Optional supporting content text'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
title: z.string().min(3).max(100).default('Image Showcase').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).default('Subtitle for the slide').optional().meta({
description: "Optional subtitle or description",
}),
image: z.string().default('https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=800&h=600&fit=crop').meta({
description: "Main image URL",
}),
imageCaption: z.string().min(5).max(200).default('Image caption').optional().meta({
description: "Optional image caption or description",
}),
content: z.string().min(10).max(600).optional().meta({
description: "Optional supporting content text",
}),
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = imageSlideSchema

View file

@ -7,12 +7,22 @@ export const layoutName = 'Process Slide'
export const layoutDescription = 'A slide with a title, subtitle, and process steps'
const processSlideSchema = z.object({
title: z.string().min(3).max(100).default('Our Process').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
title: z.string().min(3).max(100).default('Our Process').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
processSteps: z.array(z.object({
step: z.number().min(1).max(10).describe('Step number'),
title: z.string().min(3).max(100).describe('Step title'),
description: z.string().min(10).max(200).describe('Step description')
step: z.number().min(1).max(10).meta({
description: "Step number",
}),
title: z.string().min(3).max(100).meta({
description: "Step title",
}),
description: z.string().min(10).max(200).meta({
description: "Step description",
})
})).min(2).max(6).default([
{
step: 1,
@ -35,7 +45,9 @@ const processSlideSchema = z.object({
description: 'Final delivery and ongoing support'
}
]).describe('Process steps (2-6 items)'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = processSlideSchema

View file

@ -7,14 +7,30 @@ export const layoutName = 'Quote Slide'
export const layoutDescription = 'A slide with a title, subtitle, quote, author, author title, company, and author image'
const quoteSlideSchema = z.object({
title: z.string().min(3).max(100).default('Testimonials').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
quote: z.string().min(10).max(500).default('This solution has transformed our business operations and exceeded all expectations.').describe('The main quote or testimonial'),
author: z.string().min(2).max(100).default('John Smith').describe('Quote author name'),
authorTitle: z.string().min(2).max(100).optional().describe('Author job title or position'),
company: z.string().min(2).max(100).optional().describe('Author company or organization'),
authorImage: z.string().optional().describe('URL to author photo'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
title: z.string().min(3).max(100).default('Testimonials').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
quote: z.string().min(10).max(500).default('This solution has transformed our business operations and exceeded all expectations.').meta({
description: "The main quote or testimonial",
}),
author: z.string().min(2).max(100).default('John Smith').meta({
description: "Quote author name",
}),
authorTitle: z.string().min(2).max(100).optional().meta({
description: "Author job title or position",
}),
company: z.string().min(2).max(100).optional().meta({
description: "Author company or organization",
}),
authorImage: z.string().optional().meta({
description: "URL to author photo",
}),
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = quoteSlideSchema

View file

@ -7,13 +7,25 @@ export const layoutName = 'Statistics Slide'
export const layoutDescription = 'A slide with a title, subtitle, and statistics'
const statisticsSlideSchema = z.object({
title: z.string().min(3).max(100).default('Key Statistics').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
title: z.string().min(3).max(100).default('Key Statistics').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
statistics: z.array(z.object({
value: z.string().min(1).max(20).describe('Statistical value (e.g., "250%", "$1.2M", "99.9%")'),
label: z.string().min(3).max(100).describe('Description of the statistic'),
trend: z.enum(['up', 'down', 'neutral']).optional().describe('Trend direction indicator'),
context: z.string().min(5).max(200).optional().describe('Additional context or time period')
value: z.string().min(1).max(20).meta({
description: "Statistical value (e.g., '250%', '$1.2M', '99.9%')",
}),
label: z.string().min(3).max(100).meta({
description: "Description of the statistic",
}),
trend: z.enum(['up', 'down', 'neutral']).optional().meta({
description: "Trend direction indicator",
}),
context: z.string().min(5).max(200).optional().meta({
description: "Additional context or time period",
})
})).min(2).max(6).default([
{
value: '250%',
@ -40,7 +52,9 @@ const statisticsSlideSchema = z.object({
context: 'Customer service'
}
]).describe('List of statistics (2-6 items)'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = statisticsSlideSchema

View file

@ -6,15 +6,31 @@ export const layoutName = 'Team Slide'
export const layoutDescription = 'A slide with a title, subtitle, and team members'
const teamSlideSchema = z.object({
title: z.string().min(3).max(100).default('Meet Our Team').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or team description'),
title: z.string().min(3).max(100).default('Meet Our Team').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or team description",
}),
teamMembers: z.array(z.object({
name: z.string().min(2).max(100).describe('Team member name'),
title: z.string().min(2).max(100).describe('Job title or role'),
image: z.string().optional().describe('URL to team member photo'),
bio: z.string().min(10).max(300).optional().describe('Brief biography or description'),
email: z.string().email().optional().describe('Contact email'),
linkedin: z.string().url().optional().describe('LinkedIn profile URL')
name: z.string().min(2).max(100).meta({
description: "Team member name",
}),
title: z.string().min(2).max(100).meta({
description: "Job title or role",
}),
image: z.string().optional().meta({
description: "URL to team member photo",
}),
bio: z.string().min(10).max(300).optional().meta({
description: "Brief biography or description",
}),
email: z.email().optional().meta({
description: "Contact email",
}),
linkedin: z.url().optional().meta({
description: "LinkedIn profile URL",
})
})).min(1).max(6).default([
{
name: 'Sarah Johnson',

View file

@ -7,13 +7,25 @@ export const layoutName = 'Timeline Slide'
export const layoutDescription = 'A slide with a title, subtitle, and timeline items'
const timelineSlideSchema = z.object({
title: z.string().min(3).max(100).default('Project Timeline').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
title: z.string().min(3).max(100).default('Project Timeline').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
timelineItems: z.array(z.object({
date: z.string().min(2).max(50).describe('Date or time period'),
title: z.string().min(3).max(100).describe('Event or milestone title'),
description: z.string().min(10).max(300).describe('Event description'),
status: z.enum(['completed', 'current', 'upcoming']).default('upcoming').describe('Timeline item status')
date: z.string().min(2).max(50).meta({
description: "Date or time period",
}),
title: z.string().min(3).max(100).meta({
description: "Event or milestone title",
}),
description: z.string().min(10).max(300).meta({
description: "Event description",
}),
status: z.enum(['completed', 'current', 'upcoming']).default('upcoming').meta({
description: "Timeline item status",
})
})).min(2).max(6).default([
{
date: 'Q1 2024',
@ -39,8 +51,12 @@ const timelineSlideSchema = z.object({
description: 'Final deployment, go-live activities, and post-launch monitoring',
status: 'upcoming'
}
]).describe('Timeline events (2-6 items)'),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
]).meta({
description: "Timeline events (2-6 items)",
}),
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = timelineSlideSchema

View file

@ -7,23 +7,37 @@ export const layoutName = 'Two Column Slide'
export const layoutDescription = 'A slide with a title, subtitle, and two columns of content'
const twoColumnSlideSchema = z.object({
title: z.string().min(3).max(100).default('Two Column Layout').describe('Title of the slide'),
subtitle: z.string().min(3).max(150).optional().describe('Optional subtitle or description'),
title: z.string().min(3).max(100).default('Two Column Layout').meta({
description: "Title of the slide",
}),
subtitle: z.string().min(3).max(150).optional().meta({
description: "Optional subtitle or description",
}),
leftColumn: z.object({
title: z.string().min(3).max(100).default('Left Column').describe('Left column title'),
content: z.string().min(10).max(800).default('Content for the left column goes here. This can include detailed information, explanations, or supporting details.').describe('Left column content')
title: z.string().min(3).max(100).default('Left Column').meta({
description: "Left column title",
}),
content: z.string().min(10).max(800).default('Content for the left column goes here. This can include detailed information, explanations, or supporting details.').meta({
description: "Left column content",
})
}).default({
title: 'Left Column',
content: 'Content for the left column goes here. This can include detailed information, explanations, or supporting details.'
}),
rightColumn: z.object({
title: z.string().min(3).max(100).default('Right Column').describe('Right column title'),
content: z.string().min(10).max(800).default('Content for the right column goes here. This can include additional information, comparisons, or contrasting details.').describe('Right column content')
title: z.string().min(3).max(100).default('Right Column').meta({
description: "Right column title",
}),
content: z.string().min(10).max(800).default('Content for the right column goes here. This can include additional information, comparisons, or contrasting details.').meta({
description: "Right column content",
})
}).default({
title: 'Right Column',
content: 'Content for the right column goes here. This can include additional information, comparisons, or contrasting details.'
}),
backgroundImage: z.string().optional().describe('URL to background image for the slide')
backgroundImage: z.string().optional().meta({
description: "URL to background image for the slide",
})
})
export const Schema = twoColumnSlideSchema

File diff suppressed because it is too large Load diff

View file

@ -47,6 +47,7 @@
"marked": "^15.0.11",
"next": "^14.2.14",
"next-themes": "^0.4.6",
"puppeteer": "^24.13.0",
"react": "^18",
"react-dom": "^18",
"react-redux": "^9.1.2",
@ -56,8 +57,7 @@
"tailwind-scrollbar-hide": "^2.0.0",
"tailwindcss-animate": "^1.0.7",
"tiptap-markdown": "^0.8.10",
"zod": "^3.25.76",
"zod-to-json-schema": "^3.24.6"
"zod": "^4.0.5"
},
"devDependencies": {
"@types/animejs": "^3.1.12",
@ -71,5 +71,8 @@
},
"overrides": {
"brace-expansion": "2.0.2"
},
"peerDependencies": {
"zod": "^4.0.5"
}
}