Merge branch 'feat/custom_schema_and_layout' of github.com:presenton/presenton into feat/custom_schema_and_layout
This commit is contained in:
commit
ca42467bcf
19 changed files with 1125 additions and 163 deletions
|
|
@ -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">
|
||||
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ export function OutlineItem({
|
|||
|
||||
{/* Editable Markdown Content */}
|
||||
<MarkdownEditor
|
||||
key={index}
|
||||
content={slideOutline.body || ''}
|
||||
onChange={(content) => handleSlideChange({ ...slideOutline, body: content })}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
869
servers/nextjs/package-lock.json
generated
869
servers/nextjs/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue