diff --git a/servers/fastapi/utils/llm_calls/generate_presentation_outlines.py b/servers/fastapi/utils/llm_calls/generate_presentation_outlines.py index 3d0ac08f..f19c9a06 100644 --- a/servers/fastapi/utils/llm_calls/generate_presentation_outlines.py +++ b/servers/fastapi/utils/llm_calls/generate_presentation_outlines.py @@ -42,85 +42,15 @@ You are an expert presentation creator. Generate structured presentations based - Ensure titles create a **logical flow** through the presentation - Keep titles **concise but meaningful** -### Slide Body Content -- Use **full markdown formatting** for rich content structure -- Apply consistent formatting: - - `**bold**` for key concepts and emphasis - - `*italic*` for secondary emphasis or definitions - - `- or *` for bullet points and lists - - `> ` for quotes or callouts - - `### ` for subsections within slides - - ``` for code blocks (when applicable) - - `inline code` for technical terms or specific terminology - -### Content Structure Per Slide -- **Opening/Hook**: Start with engaging content -- **Main Points**: 3-5 key points maximum per slide -- **Supporting Details**: Brief explanations or examples -- **Visual Cues**: Suggest where charts, images, or diagrams would be beneficial -- **Transitions**: Natural flow to next slide topic - -### Speaker Notes -- Include **comprehensive speaker notes** for each slide -- Provide **additional context** not covered in slide content -- Add **timing suggestions** and **delivery tips** -- Include **visual element descriptions** (charts, images, icons) -- Specify if notes apply to **specific slides** or **entire presentation** -- Add **interaction opportunities** (questions, polls, discussions) - -## Quality Standards - -### Content Quality -- Ensure **factual accuracy** and **current information** -- Maintain **consistent tone** throughout presentation -- Create **logical progression** between slides -- Include **actionable insights** where appropriate -- Balance **depth and accessibility** for target audience - -### Formatting Consistency -- Use **uniform markdown styling** across all slides -- Maintain **consistent bullet point structure** -- Apply **appropriate heading levels** -- Ensure **readable content density** - -### Language and Tone -- Generate content in the **specified language** -- Adapt **tone and complexity** to target audience -- Use **active voice** and **clear, direct language** -- Include **engaging elements** (questions, scenarios, examples) ## Special Considerations ### Slide Count Compliance - Generate **exactly** the number of slides requested - Distribute content **evenly** across slides -- Ensure **no slide is significantly longer** than others - Create **balanced information flow** -### Visual Integration -- Suggest **relevant visual elements** in notes -- Indicate **optimal placement** for charts, graphs, images -- Recommend **slide layouts** for different content types -- Specify **color schemes** or **design elements** when relevant -### Interactivity Elements -- Include **audience engagement opportunities** -- Suggest **discussion points** or **questions** -- Recommend **interactive elements** (polls, breakout sessions) -- Provide **transition phrases** between sections - -## Validation Checklist - -Before finalizing, ensure: -- [ ] Exact number of slides generated -- [ ] All titles are plain text (no markdown) -- [ ] All slide bodies use proper markdown formatting -- [ ] Comprehensive notes provided for each slide -- [ ] Logical flow between slides -- [ ] Consistent formatting throughout -- [ ] Content appropriate for specified language -- [ ] No slide title appears in slide body -- [ ] Speaker notes clearly indicate scope (slide-specific or presentation-wide) """ diff --git a/servers/fastapi/utils/llm_calls/generate_slide_content.py b/servers/fastapi/utils/llm_calls/generate_slide_content.py index afca334b..fd58888b 100644 --- a/servers/fastapi/utils/llm_calls/generate_slide_content.py +++ b/servers/fastapi/utils/llm_calls/generate_slide_content.py @@ -9,7 +9,7 @@ from utils.llm_provider import ( get_small_model, is_google_selected, ) -from utils.schema_utils import remove_fields_from_schema +from utils.schema_utils import remove_fields_from_schema, generate_constraint_sentences system_prompt = """ Generate structured slide based on provided title and outline, follow mentioned steps and notes and provide structured output. @@ -19,6 +19,7 @@ system_prompt = """ 2. Generate structured slide based on the outline and title. # Notes + - **Strictly follow the max and min character limit for each property in the slide.** - Slide body should not use words like "This slide", "This presentation". - Rephrase the slide body to make it flow naturally. - Do not use markdown formatting in slide body. @@ -35,11 +36,12 @@ def get_user_prompt(title: str, outline: str): """ -def get_prompt_to_generate_slide_content(title: str, outline: str): +def get_prompt_to_generate_slide_content(title: str, outline: str, schema_constraints: str = ""): + return [ { "role": "system", - "content": system_prompt, + "content": system_prompt + f"\n{schema_constraints}", }, { "role": "user", @@ -52,6 +54,8 @@ async def get_slide_content_from_type_and_outline( slide_layout: SlideLayoutModel, outline: SlideOutlineModel ): model = get_small_model() + + schema_constraints = generate_constraint_sentences(slide_layout.json_schema) if not is_google_selected(): client = get_llm_client() @@ -60,6 +64,7 @@ async def get_slide_content_from_type_and_outline( messages=get_prompt_to_generate_slide_content( outline.title, outline.body, + schema_constraints, ), response_format={ "type": "json_schema", @@ -79,7 +84,7 @@ async def get_slide_content_from_type_and_outline( model=model, contents=[get_user_prompt(outline.title, outline.body)], config=GenerateContentConfig( - system_instruction=system_prompt, + system_instruction=system_prompt + f"\n{schema_constraints}", response_mime_type="application/json", response_json_schema=slide_layout.json_schema, ), diff --git a/servers/fastapi/utils/llm_provider.py b/servers/fastapi/utils/llm_provider.py index 7999ad4a..f4e801ea 100644 --- a/servers/fastapi/utils/llm_provider.py +++ b/servers/fastapi/utils/llm_provider.py @@ -16,6 +16,7 @@ from utils.get_env import ( ) + def get_llm_provider(): try: return LLMProvider(get_llm_provider_env()) @@ -77,9 +78,9 @@ def get_llm_api_key(): def get_llm_client(): client = AsyncOpenAI( - base_url=get_model_base_url(), - api_key=get_llm_api_key(), - ) + base_url=get_model_base_url(), + api_key=get_llm_api_key(), + ) return client diff --git a/servers/fastapi/utils/schema_utils.py b/servers/fastapi/utils/schema_utils.py index d78c8dfa..94b4642c 100644 --- a/servers/fastapi/utils/schema_utils.py +++ b/servers/fastapi/utils/schema_utils.py @@ -48,3 +48,72 @@ def remove_fields_from_schema(schema: dict, fields_to_remove: List[str]): ] return schema + + +def generate_constraint_sentences(schema: dict) -> str: + """ + Generate human-readable constraint sentences from a JSON schema. + + Args: + schema: JSON schema dictionary + + Returns: + String containing constraint sentences separated by newlines + """ + constraints = [] + + def extract_constraints_recursive(obj, prefix=""): + if isinstance(obj, dict): + if "properties" in obj: + properties = obj["properties"] + for prop_name, prop_def in properties.items(): + current_path = f"{prefix}.{prop_name}" if prefix else prop_name + + if isinstance(prop_def, dict): + prop_type = prop_def.get("type") + + # Handle string constraints + if prop_type == "string": + min_length = prop_def.get("minLength") + max_length = prop_def.get("maxLength") + + if min_length is not None and max_length is not None: + constraints.append(f" - {current_path} should be less than {max_length} characters and greater than {min_length} characters") + elif max_length is not None: + constraints.append(f" - {current_path} should be less than {max_length} characters") + elif min_length is not None: + constraints.append(f" - {current_path} should be greater than {min_length} characters") + + # Handle array constraints + elif prop_type == "array": + min_items = prop_def.get("minItems") + max_items = prop_def.get("maxItems") + + if min_items is not None and max_items is not None: + constraints.append(f" - {current_path} should have more than {min_items} items and less than {max_items} items") + elif max_items is not None: + constraints.append(f" - {current_path} should have less than {max_items} items") + elif min_items is not None: + constraints.append(f" - {current_path} should have more than {min_items} items") + + # Recurse into nested objects + if prop_type == "object" or "properties" in prop_def: + extract_constraints_recursive(prop_def, current_path) + + # Handle array items if they have properties + if prop_type == "array" and "items" in prop_def: + items_def = prop_def["items"] + if isinstance(items_def, dict) and ("properties" in items_def or items_def.get("type") == "object"): + extract_constraints_recursive(items_def, f"{current_path}[*]") + + # Also recurse into other nested structures + for key, value in obj.items(): + if key not in ["properties", "type", "minLength", "maxLength", "minItems", "maxItems"] and isinstance(value, dict): + extract_constraints_recursive(value, prefix) + + # Start extraction from the root schema + extract_constraints_recursive(schema) + + return "\n".join(constraints) + + diff --git a/servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx b/servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx index 9c63e419..81197127 100644 --- a/servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx +++ b/servers/nextjs/app/(presentation-generator)/pdf-maker/PdfMakerPage.tsx @@ -76,6 +76,7 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => {
@@ -102,7 +103,6 @@ const PresentationPage = ({ presentation_id }: { presentation_id: string }) => { presentationData.slides.length > 0 && presentationData.slides.map((slide: any, index: number) => (
- {renderSlideContent(slide, false)}
))} diff --git a/servers/nextjs/app/api/presentation_to_pptx_model/route.ts b/servers/nextjs/app/api/presentation_to_pptx_model/route.ts index eb51b36c..5ae94572 100644 --- a/servers/nextjs/app/api/presentation_to_pptx_model/route.ts +++ b/servers/nextjs/app/api/presentation_to_pptx_model/route.ts @@ -1,15 +1,13 @@ import { ApiError } from "@/models/errors"; import { NextRequest, NextResponse } from "next/server"; -import puppeteer, { Browser, ElementHandle } from "puppeteer"; +import puppeteer, { Browser, ElementHandle, Page } from "puppeteer"; import { ElementAttributes, SlideAttributesResult } from "@/types/element_attibutes"; import { convertElementAttributesToPptxSlides } from "@/utils/pptx_models_utils"; import { PptxPresentationModel } from "@/types/pptx_models"; import fs from "fs"; import path from "path"; -import crypto from "crypto"; -import sharp from "sharp"; +import { v4 as uuidv4 } from 'uuid'; -// Interface for getAllChildElementsAttributes function arguments interface GetAllChildElementsAttributesArgs { element: ElementHandle; rootRect?: { left: number; top: number; width: number; height: number } | null; @@ -23,41 +21,27 @@ interface GetAllChildElementsAttributesArgs { export async function GET(request: NextRequest) { let browser: Browser | null = null; + let page: Page | null = null; + try { const id = await getPresentationId(request); - browser = await puppeteer.launch({ - headless: true, - args: ['--no-sandbox', '--disable-setuid-sandbox'] - }); + [browser, page] = await getBrowserAndPage(id); + const screenshotsDir = getScreenshotsDir(); - // Ensure screenshots directory exists - const tempDir = process.env.TEMP_DIRECTORY; - if (!tempDir) { - console.warn('TEMP_DIRECTORY environment variable not set, skipping screenshot'); - return undefined; - } - const screenshotsDir = path.join(tempDir, 'screenshots'); - if (!fs.existsSync(screenshotsDir)) { - fs.mkdirSync(screenshotsDir, { recursive: true }); - } - - const slides = await getSlides(browser, id); + const slides = await getSlides(page); const slides_attributes = await getSlidesAttributes(slides, screenshotsDir); - const slides_pptx_models = convertElementAttributesToPptxSlides(slides_attributes.elements, slides_attributes.backgroundColors); + await postProcessSlidesAttributes(slides_attributes, screenshotsDir); + const slides_pptx_models = convertElementAttributesToPptxSlides(slides_attributes); const presentation_pptx_model: PptxPresentationModel = { slides: slides_pptx_models, }; - if (browser) { - await browser.close(); - } + await closeBrowserAndPage(browser, page); return NextResponse.json(presentation_pptx_model); } catch (error: any) { console.error(error); - if (browser) { - await browser.close(); - } + await closeBrowserAndPage(browser, page); if (error instanceof ApiError) { return NextResponse.json(error, { status: 400 }); } @@ -73,42 +57,19 @@ async function getPresentationId(request: NextRequest) { return id; } -async function getSlidesAttributes(slides: ElementHandle[], screenshotsDir: string) { - const slideResults: SlideAttributesResult[] = []; - //? Can't use Promise.all because of the screenshot - //? taking screenshot with mess up position of elements - for (const slide of slides) { - const result = await getAllChildElementsAttributes({ element: slide, screenshotsDir }); - slideResults.push(result); - } +async function getBrowserAndPage(id: string): Promise<[Browser, Page]> { + const browser = await puppeteer.launch({ + headless: true, + args: [ + '--no-sandbox', + '--disable-setuid-sandbox', + '--disable-dev-shm-usage', + '--disable-gpu', + '--disable-web-security', + '--window-size=1920,1080' + ], + }); - const elements = slideResults.map(result => result.elements); - const backgroundColors = slideResults.map(result => result.backgroundColor); - - return { - elements, - backgroundColors - }; -} - - -async function getSlides(browser: Browser, id: string) { - const slides_wrapper = await getSlidesWrapper(browser, id); - const slides = await slides_wrapper.$$(":scope > div > div"); - return slides; -} - -async function getSlidesWrapper(browser: Browser, id: string): Promise> { - const page = await getPresentationPage(browser, id); - const slides_wrapper = await page.$("#presentation-slides-wrapper"); - if (!slides_wrapper) { - throw new ApiError("Presentation slides not found"); - } - return slides_wrapper; -} - - -async function getPresentationPage(browser: Browser, id: string) { const page = await browser.newPage(); page.on('console', (msg) => { @@ -118,16 +79,116 @@ async function getPresentationPage(browser: Browser, id: string) { }); await page.setViewport({ width: 1920, height: 1080, deviceScaleFactor: 1 }); - await page.goto(`http://localhost/presentation?id=${id}`, { + await page.goto(`http://localhost/pdf-maker?id=${id}`, { waitUntil: "networkidle0", timeout: 60000, }); - return page; + return [browser, page]; +} + +async function closeBrowserAndPage(browser: Browser | null, page: Page | null) { + await page?.close(); + await browser?.close(); +} + +function getScreenshotsDir() { + const tempDir = process.env.TEMP_DIRECTORY; + if (!tempDir) { + console.warn('TEMP_DIRECTORY environment variable not set, skipping screenshot'); + throw new ApiError('TEMP_DIRECTORY environment variable not set'); + } + const screenshotsDir = path.join(tempDir, 'screenshots'); + if (!fs.existsSync(screenshotsDir)) { + fs.mkdirSync(screenshotsDir, { recursive: true }); + } + return screenshotsDir; +} + +async function postProcessSlidesAttributes(slidesAttributes: SlideAttributesResult[], screenshotsDir: string) { + for (const slideAttributes of slidesAttributes) { + for (const element of slideAttributes.elements) { + if (element.should_screenshot) { + const screenshotPath = await screenshotElement(element, screenshotsDir); + element.imageSrc = screenshotPath; + element.should_screenshot = false; + element.element = undefined; + } + } + } +} + +async function screenshotElement(element: ElementAttributes, screenshotsDir: string) { + const screenshotPath = path.join(screenshotsDir, `${uuidv4()}.png`) as `${string}.png`; + + // Hide all elements except the target element and its ancestors + await element.element?.evaluate((el) => { + const originalDisplays = new Map(); + + const hideAllExcept = (targetElement: Element) => { + const allElements = document.querySelectorAll('*'); + + allElements.forEach((elem) => { + if (targetElement === elem || targetElement.contains(elem) || elem.contains(targetElement)) { + return; + } + + const computedStyle = window.getComputedStyle(elem); + originalDisplays.set(elem, computedStyle.display); + (elem as HTMLElement).style.display = 'none'; + }); + }; + + hideAllExcept(el); + + (el as any).__restoreDisplays = () => { + originalDisplays.forEach((display, elem) => { + (elem as HTMLElement).style.display = display; + }); + }; + }); + + const screenshot = await element.element?.screenshot({ path: screenshotPath }); + if (!screenshot) { + throw new ApiError("Failed to screenshot element"); + } + + await element.element?.evaluate((el) => { + if ((el as any).__restoreDisplays) { + (el as any).__restoreDisplays(); + } + }); + + return screenshotPath; } +async function getSlidesAttributes(slides: ElementHandle[], screenshotsDir: string): Promise { + const slideAttributes = await Promise.all(slides.map(async (slide) => { + return await getAllChildElementsAttributes({ element: slide, screenshotsDir }); + })); + + return slideAttributes; +} + + +async function getSlides(page: Page) { + const slides_wrapper = await getSlidesWrapper(page); + const slides = await slides_wrapper.$$(":scope > div > div"); + return slides; +} + +async function getSlidesWrapper(page: Page): Promise> { + const slides_wrapper = await page.$("#presentation-slides-wrapper"); + if (!slides_wrapper) { + throw new ApiError("Presentation slides not found"); + } + return slides_wrapper; +} + + + + async function getAllChildElementsAttributes({ element, rootRect = null, depth = 0, inheritedFont, inheritedBackground, inheritedBorderRadius, screenshotsDir }: GetAllChildElementsAttributesArgs): Promise { - // Get rootRect if not provided (first call) const currentRootRect = rootRect || await element.evaluate((el) => { const rect = el.getBoundingClientRect(); return { @@ -138,67 +199,23 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth = }; }); - // Check if this element is SVG or canvas or table - const tagName = await element.evaluate((el) => el.tagName.toLowerCase()); - - - if (tagName === 'svg' || tagName === 'canvas' || tagName === 'table') { - return { - elements: [], - backgroundColor: undefined - }; - - // // Get basic attributes for the element - // const attributes = await getElementAttributes(element); - // // Take screenshot of SVG/canvas/table element with accurate colors and opacity - // const screenshotPath = await takeElementScreenshot(element, screenshotsDir); - - // // Update image source to point to the screenshot - // if (screenshotPath) { - // attributes.imageSrc = screenshotPath; - // } - - // // Adjust position relative to root - // if (attributes.position && attributes.position.left !== undefined && attributes.position.top !== undefined) { - // attributes.position = { - // left: attributes.position.left - currentRootRect.left, - // top: attributes.position.top - currentRootRect.top, - // width: attributes.position.width, - // height: attributes.position.height, - // }; - // } - - // // Return early without processing children for these elements - // return { - // elements: [attributes], - // backgroundColor: undefined - // }; - } - - // Get direct children only (not all descendants) const directChildElementHandles = await element.$$(':scope > *'); const allResults: { attributes: ElementAttributes; depth: number }[] = []; - // Process direct children recursively for (const childElementHandle of directChildElementHandles) { - // Get attributes for current child const attributes = await getElementAttributes(childElementHandle); - // Apply inherited font only on elements that have direct text if (inheritedFont && !attributes.font && attributes.innerText && attributes.innerText.trim().length > 0) { attributes.font = inheritedFont; } - // Apply inherited background only on elements that have shadow if (inheritedBackground && !attributes.background && attributes.shadow) { attributes.background = inheritedBackground; } - // Apply inherited border radius if element doesn't have it if (inheritedBorderRadius && !attributes.borderRadius) { attributes.borderRadius = inheritedBorderRadius; } - // Adjust position relative to root if (attributes.position && attributes.position.left !== undefined && attributes.position.top !== undefined) { attributes.position = { left: attributes.position.left - currentRootRect.left, @@ -208,10 +225,18 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth = }; } - // Add current child to results + if (attributes.tagName === 'svg' || attributes.tagName === 'canvas' || attributes.tagName === 'table') { + attributes.should_screenshot = true; + attributes.element = childElementHandle; + } + allResults.push({ attributes, depth }); - // Recursively process children of this child + //? If the element is a svg, canvas, or table, we don't need to go deeper + if (attributes.should_screenshot) { + break; + } + const childResults = await getAllChildElementsAttributes({ element: childElementHandle, rootRect: currentRootRect, @@ -224,7 +249,6 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth = allResults.push(...childResults.elements.map(attr => ({ attributes: attr, depth: depth + 1 }))); } - // Find background color from elements with root position (only in first call) let backgroundColor: string | undefined; if (!rootRect) { const elementsWithRootPosition = allResults.filter(({ attributes }) => { @@ -243,13 +267,15 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth = } } - // Filter results (only in first call) const filteredResults = !rootRect ? allResults.filter(({ attributes }) => { const hasBackground = attributes.background && attributes.background.color; const hasBorder = attributes.border && attributes.border.color; const hasShadow = attributes.shadow && attributes.shadow.color; const hasText = attributes.innerText && attributes.innerText.trim().length > 0; const hasImage = attributes.imageSrc; + const isSvg = attributes.tagName === 'svg'; + const isCanvas = attributes.tagName === 'canvas'; + const isTable = attributes.tagName === 'table'; const isRootPosition = attributes.position && attributes.position.left === 0 && @@ -257,7 +283,7 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth = attributes.position.width === currentRootRect.width && attributes.position.height === currentRootRect.height; - const hasOtherProperties = hasBackground || hasBorder || hasShadow || hasText || hasImage; + const hasOtherProperties = hasBackground || hasBorder || hasShadow || hasText || hasImage || isSvg || isCanvas || isTable; return hasOtherProperties && !isRootPosition; }) : allResults; @@ -274,7 +300,6 @@ async function getAllChildElementsAttributes({ element, rootRect = null, depth = return zIndexB - zIndexA; }) .map(({ attributes }) => { - // Set background color to backgroundColor for elements that have shadow but no background color if (attributes.shadow && attributes.shadow.color && (!attributes.background || !attributes.background.color) && backgroundColor) { attributes.background = { color: backgroundColor, @@ -416,8 +441,6 @@ async function getElementAttributes(element: ElementHandle): Promise): Promise): Promise): Promise): Promise): Promise): Promise 0) { const shadowColor = colorParts.join(' '); @@ -509,34 +523,28 @@ async function getElementAttributes(element: ElementHandle): Promise value !== 0); - // Calculate a score for this shadow (higher is better) let shadowScore = 0; if (hasNonZeroValues) { - // Count non-zero numeric values shadowScore += numericParts.filter(value => value !== 0).length; } if (hasVisibleColor) { - shadowScore += 2; // Bonus for visible color + shadowScore += 2; } - // Select this shadow if it has a better score if ((hasNonZeroValues || hasVisibleColor) && shadowScore > bestShadowScore) { selectedShadow = shadowStr; bestShadowScore = shadowScore; } } - // If no meaningful shadow found, use the first one if (!selectedShadow && shadows.length > 0) { selectedShadow = shadows[0]; } if (selectedShadow) { - // Parse the selected shadow const shadowParts = selectedShadow.split(' '); const numericParts: number[] = []; const colorParts: string[] = []; @@ -544,7 +552,6 @@ async function getElementAttributes(element: ElementHandle): Promise): Promise): Promise= 2) { const offsetX = numericParts[0]; const offsetY = numericParts[1]; const blurRadius = numericParts.length >= 3 ? numericParts[2] : 0; const spreadRadius = numericParts.length >= 4 ? numericParts[3] : 0; - // Handle color - it can be anywhere in the parts let shadowColor = 'rgba(0, 0, 0, 0.3)'; // default color if (colorParts.length > 0) { shadowColor = colorParts.join(' '); @@ -602,7 +603,6 @@ async function getElementAttributes(element: ElementHandle): Promise 0 || spreadRadius !== 0 || (shadowColorResult.hex && shadowColorResult.hex !== '000000' && shadowColorResult.opacity !== 0); @@ -650,24 +650,19 @@ async function getElementAttributes(element: ElementHandle): Promise singleLineHeight * 2; // Allow some tolerance + const hasTextWrapping = htmlEl.offsetHeight > singleLineHeight * 2; const hasOverflow = htmlEl.scrollHeight > htmlEl.clientHeight; const isMultiline = hasExplicitLineBreaks || hasTextWrapping || hasOverflow; - // Only return line height if text is multiline if (isMultiline && lineHeight && lineHeight !== 'normal') { const parsedLineHeight = parseFloat(lineHeight); if (!isNaN(parsedLineHeight)) { @@ -804,53 +799,3 @@ async function getElementAttributes(element: ElementHandle): Promise, screenshotsDir: string): Promise { - try { - // Check element visibility and dimensions - const elementInfo = await element.evaluate((el) => { - const rect = el.getBoundingClientRect(); - const styles = window.getComputedStyle(el); - - // Check if element is visible - const isVisible = styles.visibility !== 'hidden' && - styles.display !== 'none' && - styles.opacity !== '0'; - - if (!isVisible || rect.width <= 0 || rect.height <= 0) { - return null; - } - - return { - width: rect.width, - height: rect.height - }; - }); - - if (!elementInfo) { - console.warn('Element is not visible or has invalid dimensions, skipping screenshot'); - return undefined; - } - - // Generate unique filename - const uuid = crypto.randomUUID(); - const filename = `${uuid}.png`; - const filePath = path.join(screenshotsDir, filename); - - // Take screenshot of the element with accurate colors and opacity - // This captures the element exactly as rendered in the browser with all CSS styles applied - await element.screenshot({ - path: filePath as `${string}.png`, - type: 'png', - omitBackground: true // Use transparent background for better quality - }); - - console.log(`Screenshot saved: ${filePath}`); - return filePath; - - } catch (error) { - console.error('Error taking element screenshot:', error); - return undefined; - } -} - diff --git a/servers/nextjs/package-lock.json b/servers/nextjs/package-lock.json index d5f02b94..f6bab00e 100644 --- a/servers/nextjs/package-lock.json +++ b/servers/nextjs/package-lock.json @@ -35,7 +35,6 @@ "@tiptap/extension-underline": "^2.0.0", "@tiptap/react": "^2.11.5", "@tiptap/starter-kit": "^2.11.5", - "animejs": "^3.2.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.0", @@ -44,19 +43,17 @@ "marked": "^15.0.11", "next": "^14.2.14", "next-themes": "^0.4.6", - "pptxgenjs": "^4.0.1", "puppeteer": "^24.13.0", "react": "^18", "react-dom": "^18", - "react-element-to-jsx-string": "^15.0.0", "react-redux": "^9.1.2", "recharts": "^2.15.4", - "sharp": "^0.34.3", "sonner": "^2.0.6", "tailwind-merge": "^2.5.3", "tailwind-scrollbar-hide": "^2.0.0", "tailwindcss-animate": "^1.0.7", "tiptap-markdown": "^0.8.10", + "uuid": "^11.1.0", "zod": "^4.0.5" }, "devDependencies": { @@ -65,7 +62,6 @@ "@types/puppeteer": "^5.4.7", "@types/react": "^18", "@types/react-dom": "^18", - "@types/sharp": "^0.32.0", "@types/uuid": "^10.0.0", "cypress": "^14.3.3", "tailwindcss": "^3.4.1", @@ -119,12 +115,6 @@ "node": ">=6.9.0" } }, - "node_modules/@base2/pretty-print-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz", - "integrity": "sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==", - "license": "BSD-2-Clause" - }, "node_modules/@cypress/request": { "version": "3.0.8", "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz", @@ -238,15 +228,6 @@ "react": ">=16.8.0" } }, - "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@floating-ui/core": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.2.tgz", @@ -285,402 +266,6 @@ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", "license": "MIT" }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.3.tgz", - "integrity": "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.0" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.3.tgz", - "integrity": "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.0" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.0.tgz", - "integrity": "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.0.tgz", - "integrity": "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.0.tgz", - "integrity": "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.0.tgz", - "integrity": "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.0.tgz", - "integrity": "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.0.tgz", - "integrity": "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.0.tgz", - "integrity": "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.0.tgz", - "integrity": "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.0.tgz", - "integrity": "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.3.tgz", - "integrity": "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.3.tgz", - "integrity": "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.3.tgz", - "integrity": "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.3.tgz", - "integrity": "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.0" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.3.tgz", - "integrity": "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.0" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.3.tgz", - "integrity": "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.3.tgz", - "integrity": "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.0" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.3.tgz", - "integrity": "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.4.4" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.3.tgz", - "integrity": "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.3.tgz", - "integrity": "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.3.tgz", - "integrity": "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2783,16 +2368,6 @@ "@types/react": "^18.0.0" } }, - "node_modules/@types/sharp": { - "version": "0.32.0", - "resolved": "https://registry.npmjs.org/@types/sharp/-/sharp-0.32.0.tgz", - "integrity": "sha512-OOi3kL+FZDnPhVzsfD37J88FNeZh6gQsGcLc95NbeURRGvmSjeXiDcyWzF2o3yh/gQAUn2uhh/e+CPCa5nwAxw==", - "deprecated": "This is a stub types definition. sharp provides its own type definitions, so you do not need this installed.", - "dev": true, - "dependencies": { - "sharp": "*" - } - }, "node_modules/@types/sinonjs__fake-timers": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", @@ -2852,12 +2427,6 @@ "node": ">=8" } }, - "node_modules/animejs": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/animejs/-/animejs-3.2.2.tgz", - "integrity": "sha512-Ao95qWLpDPXXM+WrmwcKbl6uNlC5tjnowlaRYtuVDHHoygjtIPfDUoK9NthrlZsQSKjZXlmji2TrBUAVbiH0LQ==", - "license": "MIT" - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -3597,18 +3166,6 @@ "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3627,15 +3184,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -3691,6 +3239,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, "license": "MIT" }, "node_modules/cosmiconfig": { @@ -4014,14 +3563,6 @@ "node": ">=0.4.0" } }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "engines": { - "node": ">=8" - } - }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -4819,12 +4360,6 @@ "node": ">=0.10" } }, - "node_modules/https": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https/-/https-1.0.0.tgz", - "integrity": "sha512-4EC57ddXrkaF0x83Oj8sM6SLQHAWXw90Skqu2M4AEWENZ3F02dFJE/GARA8igO79tcgYqGrD7ae4f5L3um2lgg==", - "license": "ISC" - }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -4869,27 +4404,6 @@ ], "license": "BSD-3-Clause" }, - "node_modules/image-size": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", - "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", - "license": "MIT", - "dependencies": { - "queue": "6.0.2" - }, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "license": "MIT" - }, "node_modules/immer": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", @@ -4926,12 +4440,6 @@ "node": ">=8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, "node_modules/ini": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", @@ -5063,15 +4571,6 @@ "node": ">=8" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", @@ -5105,12 +4604,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5230,18 +4723,6 @@ "verror": "1.10.0" } }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "license": "(MIT OR GPL-3.0-or-later)", - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, "node_modules/lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", @@ -5252,15 +4733,6 @@ "node": "> 0.8" } }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "license": "MIT", - "dependencies": { - "immediate": "~3.0.5" - } - }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -5848,12 +5320,6 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "license": "(MIT AND Zlib)" - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6091,27 +5557,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, - "node_modules/pptxgenjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pptxgenjs/-/pptxgenjs-4.0.1.tgz", - "integrity": "sha512-TeJISr8wouAuXw4C1F/mC33xbZs/FuEG6nH9FG1Zj+nuPcGMP5YRHl6X+j3HSUnS1f3at6k75ZZXPMZlA5Lj9A==", - "license": "MIT", - "dependencies": { - "@types/node": "^22.8.1", - "https": "^1.0.0", - "image-size": "^1.2.1", - "jszip": "^3.10.1" - } - }, - "node_modules/pptxgenjs/node_modules/@types/node": { - "version": "22.16.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.4.tgz", - "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -6135,12 +5580,6 @@ "node": ">= 0.6.0" } }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -6479,15 +5918,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6533,27 +5963,6 @@ "react": "^18.3.1" } }, - "node_modules/react-element-to-jsx-string": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz", - "integrity": "sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==", - "license": "MIT", - "dependencies": { - "@base2/pretty-print-object": "1.0.1", - "is-plain-object": "5.0.0", - "react-is": "18.1.0" - }, - "peerDependencies": { - "react": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", - "react-dom": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0" - } - }, - "node_modules/react-element-to-jsx-string/node_modules/react-is": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", - "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", - "license": "MIT" - }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -6692,27 +6101,6 @@ "pify": "^2.3.0" } }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -6945,53 +6333,6 @@ "node": ">=10" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "license": "MIT" - }, - "node_modules/sharp": { - "version": "0.34.3", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.3.tgz", - "integrity": "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==", - "hasInstallScript": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.4", - "semver": "^7.7.2" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.3", - "@img/sharp-darwin-x64": "0.34.3", - "@img/sharp-libvips-darwin-arm64": "1.2.0", - "@img/sharp-libvips-darwin-x64": "1.2.0", - "@img/sharp-libvips-linux-arm": "1.2.0", - "@img/sharp-libvips-linux-arm64": "1.2.0", - "@img/sharp-libvips-linux-ppc64": "1.2.0", - "@img/sharp-libvips-linux-s390x": "1.2.0", - "@img/sharp-libvips-linux-x64": "1.2.0", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", - "@img/sharp-libvips-linuxmusl-x64": "1.2.0", - "@img/sharp-linux-arm": "0.34.3", - "@img/sharp-linux-arm64": "0.34.3", - "@img/sharp-linux-ppc64": "0.34.3", - "@img/sharp-linux-s390x": "0.34.3", - "@img/sharp-linux-x64": "0.34.3", - "@img/sharp-linuxmusl-arm64": "0.34.3", - "@img/sharp-linuxmusl-x64": "0.34.3", - "@img/sharp-wasm32": "0.34.3", - "@img/sharp-win32-arm64": "0.34.3", - "@img/sharp-win32-ia32": "0.34.3", - "@img/sharp-win32-x64": "0.34.3" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7096,19 +6437,6 @@ "dev": true, "license": "ISC" }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" - }, "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", @@ -7251,21 +6579,6 @@ "bare-events": "^2.2.0" } }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -7817,6 +7130,7 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, "license": "MIT" }, "node_modules/universalify": { @@ -7897,6 +7211,18 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", diff --git a/servers/nextjs/package.json b/servers/nextjs/package.json index 9e80adf7..109e3de8 100644 --- a/servers/nextjs/package.json +++ b/servers/nextjs/package.json @@ -52,13 +52,13 @@ "react-dom": "^18", "react-redux": "^9.1.2", "recharts": "^2.15.4", - "sharp": "^0.34.3", "sonner": "^2.0.6", "tailwind-merge": "^2.5.3", "tailwind-scrollbar-hide": "^2.0.0", "tailwindcss-animate": "^1.0.7", "tiptap-markdown": "^0.8.10", "ws": "^8.18.0", + "uuid": "^11.1.0", "zod": "^4.0.5" }, "devDependencies": { @@ -67,7 +67,6 @@ "@types/puppeteer": "^5.4.7", "@types/react": "^18", "@types/react-dom": "^18", - "@types/sharp": "^0.32.0", "@types/uuid": "^10.0.0", "@types/ws": "^8.5.13", "cypress": "^14.3.3", diff --git a/servers/nextjs/presentation-layouts/general/BasicInfoSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/BasicInfoSlideLayout.tsx index 9b9d1ff4..bff4a2d5 100644 --- a/servers/nextjs/presentation-layouts/general/BasicInfoSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/BasicInfoSlideLayout.tsx @@ -7,10 +7,10 @@ export const layoutName = 'Basic Info' export const layoutDescription = 'A clean slide layout with title, description text, and a supporting image.' const basicInfoSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Product Overview').meta({ + title: z.string().min(3).max(50).default('Product Overview').meta({ description: "Main title of the slide", }), - description: z.string().min(10).max(800).default('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.').meta({ + description: z.string().min(10).max(180).default('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.').meta({ description: "Main description text content", }), image: ImageSchema.default({ diff --git a/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx b/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx index 6bb423a6..ff17b2b3 100644 --- a/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/BulletIconsOnlySlideLayout.tsx @@ -7,7 +7,7 @@ export const layoutName = 'Bullet Icons Only' export const layoutDescription = 'A slide layout with title, grid of bullet points with icons (no descriptions), and a supporting image.' const bulletIconsOnlySlideSchema = z.object({ - title: z.string().min(3).max(100).default('Solutions').meta({ + title: z.string().min(3).max(50).default('Solutions').meta({ description: "Main title of the slide", }), image: ImageSchema.default({ @@ -17,10 +17,10 @@ const bulletIconsOnlySlideSchema = z.object({ description: "Supporting image for the slide", }), bulletPoints: z.array(z.object({ - title: z.string().min(2).max(100).meta({ + title: z.string().min(2).max(80).meta({ description: "Bullet point title", }), - subtitle: z.string().min(5).max(200).optional().meta({ + subtitle: z.string().min(5).max(180).optional().meta({ description: "Optional short subtitle or brief explanation", }), icon: IconSchema, diff --git a/servers/nextjs/presentation-layouts/general/BulletWithIconsSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/BulletWithIconsSlideLayout.tsx index 2c244ca2..79c45371 100644 --- a/servers/nextjs/presentation-layouts/general/BulletWithIconsSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/BulletWithIconsSlideLayout.tsx @@ -7,10 +7,10 @@ export const layoutName = 'Bullet with Icons' export const layoutDescription = 'A bullets style slide with main content, supporting image, and bullet points with icons and descriptions.' const bulletWithIconsSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Problem').meta({ + title: z.string().min(3).max(50).default('Problem').meta({ description: "Main title of the slide", }), - description: z.string().min(10).max(500).default('Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.').meta({ + description: z.string().max(180).default('Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.').meta({ description: "Main description text explaining the problem or topic", }), image: ImageSchema.default({ @@ -20,10 +20,10 @@ const bulletWithIconsSlideSchema = z.object({ description: "Supporting image for the slide", }), bulletPoints: z.array(z.object({ - title: z.string().min(2).max(100).meta({ + title: z.string().min(2).max(80).meta({ description: "Bullet point title", }), - description: z.string().min(10).max(300).meta({ + description: z.string().min(10).max(180).meta({ description: "Bullet point description", }), icon: IconSchema, diff --git a/servers/nextjs/presentation-layouts/general/ChartWithBulletsSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/ChartWithBulletsSlideLayout.tsx index 4dcbb7b6..5e766352 100644 --- a/servers/nextjs/presentation-layouts/general/ChartWithBulletsSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/ChartWithBulletsSlideLayout.tsx @@ -17,10 +17,10 @@ const chartDataSchema = z.object({ }); const chartWithBulletsSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Market Size').meta({ + title: z.string().min(3).max(50).default('Market Size').meta({ description: "Main title of the slide", }), - description: z.string().min(10).max(500).default('Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.').meta({ + description: z.string().min(10).max(180).default('Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.').meta({ description: "Description text below the title", }), chartType: z.enum(['bar', 'line', 'pie', 'area', 'scatter']).default('bar').meta({ @@ -51,10 +51,10 @@ const chartWithBulletsSlideSchema = z.object({ description: "Whether to show chart tooltip", }), bulletPoints: z.array(z.object({ - title: z.string().min(2).max(100).meta({ + title: z.string().min(2).max(80).meta({ description: "Bullet point title", }), - description: z.string().min(10).max(300).meta({ + description: z.string().min(10).max(180).meta({ description: "Bullet point description", }), icon: IconSchema, diff --git a/servers/nextjs/presentation-layouts/general/IntroSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/IntroSlideLayout.tsx index a458f450..a5af9844 100644 --- a/servers/nextjs/presentation-layouts/general/IntroSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/IntroSlideLayout.tsx @@ -7,10 +7,10 @@ export const layoutName = 'Intro Slide' export const layoutDescription = 'A clean slide layout with title, description text, presenter info, and a supporting image.' const introSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Product Overview').meta({ + title: z.string().min(3).max(50).default('Product Overview').meta({ description: "Main title of the slide", }), - description: z.string().min(10).max(800).default('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.').meta({ + description: z.string().min(10).max(180).default('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.').meta({ description: "Main description text content", }), presenterName: z.string().min(2).max(50).default('John Doe').meta({ diff --git a/servers/nextjs/presentation-layouts/general/MetricsWithImageSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/MetricsWithImageSlideLayout.tsx index 26f5fb7a..155e509d 100644 --- a/servers/nextjs/presentation-layouts/general/MetricsWithImageSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/MetricsWithImageSlideLayout.tsx @@ -7,10 +7,10 @@ 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(100).default('Competitive Advantage').meta({ + title: z.string().min(3).max(50).default('Competitive Advantage').meta({ description: "Main title of the slide", }), - description: z.string().min(10).max(600).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({ + description: z.string().min(10).max(180).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({ description: "Description text below the title", }), image: ImageSchema.default({ diff --git a/servers/nextjs/presentation-layouts/general/NumberedBulletsSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/NumberedBulletsSlideLayout.tsx index 95a0091f..ab64c864 100644 --- a/servers/nextjs/presentation-layouts/general/NumberedBulletsSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/NumberedBulletsSlideLayout.tsx @@ -7,7 +7,7 @@ export const layoutName = 'Numbered Bullets' export const layoutDescription = 'A slide layout with large title, supporting image, and numbered bullet points with descriptions.' const numberedBulletsSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Market Validation').meta({ + title: z.string().min(3).max(50).default('Market Validation').meta({ description: "Main title of the slide", }), image: ImageSchema.default({ @@ -17,10 +17,10 @@ const numberedBulletsSlideSchema = z.object({ description: "Supporting image for the slide", }), bulletPoints: z.array(z.object({ - title: z.string().min(2).max(100).meta({ + title: z.string().min(2).max(80).meta({ description: "Bullet point title", }), - description: z.string().min(10).max(300).meta({ + description: z.string().min(10).max(180).meta({ description: "Bullet point description", }), })).min(1).max(4).default([ diff --git a/servers/nextjs/presentation-layouts/general/TableOfContentsSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/TableOfContentsSlideLayout.tsx index 83d7eb9e..408bb6d0 100644 --- a/servers/nextjs/presentation-layouts/general/TableOfContentsSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/TableOfContentsSlideLayout.tsx @@ -10,7 +10,7 @@ const tableOfContentsSlideSchema = z.object({ number: z.number().min(1).meta({ description: "Section number" }), - title: z.string().min(1).max(100).meta({ + title: z.string().min(1).max(80).meta({ description: "Section title" }), pageNumber: z.string().min(1).max(10).meta({ diff --git a/servers/nextjs/presentation-layouts/general/TeamSlideLayout.tsx b/servers/nextjs/presentation-layouts/general/TeamSlideLayout.tsx index 1307ab5e..060cd320 100644 --- a/servers/nextjs/presentation-layouts/general/TeamSlideLayout.tsx +++ b/servers/nextjs/presentation-layouts/general/TeamSlideLayout.tsx @@ -13,17 +13,17 @@ const teamMemberSchema = z.object({ position: z.string().min(2).max(50).meta({ description: "Job title or position" }), - description: z.string().min(10).max(120).meta({ + description: z.string().max(180).meta({ description: "Brief description of the team member (around 100 characters)" }), image: ImageSchema }); const teamSlideSchema = z.object({ - title: z.string().min(3).max(100).default('Our Team Members').meta({ - description: "Main title of the slide", + title: z.string().min(3).max(50).default('Our Team Members').meta({ + description: "Main title of the slide", }), - companyDescription: z.string().min(10).max(600).default('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.').meta({ + companyDescription: z.string().min(10).max(180).default('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.').meta({ description: "Company description or team introduction text", }), teamMembers: z.array(teamMemberSchema).min(2).max(6).default([ diff --git a/servers/nextjs/types/element_attibutes.ts b/servers/nextjs/types/element_attibutes.ts index df113fc1..70df71f7 100644 --- a/servers/nextjs/types/element_attibutes.ts +++ b/servers/nextjs/types/element_attibutes.ts @@ -1,3 +1,5 @@ +import { ElementHandle } from "puppeteer"; + export interface ElementAttributes { tagName: string; id?: string; @@ -57,6 +59,8 @@ export interface ElementAttributes { shape?: 'rectangle' | 'circle'; connectorType?: string; textWrap?: boolean; + should_screenshot?: boolean; + element?: ElementHandle; } export interface SlideAttributesResult { diff --git a/servers/nextjs/utils/pptx_models_utils.ts b/servers/nextjs/utils/pptx_models_utils.ts index 35466f86..ec8d3631 100644 --- a/servers/nextjs/utils/pptx_models_utils.ts +++ b/servers/nextjs/utils/pptx_models_utils.ts @@ -1,4 +1,4 @@ -import { ElementAttributes } from "@/types/element_attibutes"; +import { ElementAttributes, SlideAttributesResult } from "@/types/element_attibutes"; import { PptxSlideModel, PptxTextBoxModel, @@ -63,15 +63,14 @@ function convertLineHeightToRelative(lineHeight?: number, fontSize?: number): nu } /** - * Converts ElementAttributes[][] to PptxSlideModel[] - * Each inner array represents elements on a slide + * Converts SlideAttributesResult[] to PptxSlideModel[] + * Each SlideAttributesResult represents elements on a slide */ export function convertElementAttributesToPptxSlides( - slidesAttributes: ElementAttributes[][], - backgroundColors?: (string | undefined)[] + slidesAttributes: SlideAttributesResult[] ): PptxSlideModel[] { - return slidesAttributes.map((slideElements, index) => { - const shapes = slideElements.map(element => { + return slidesAttributes.map((slideAttributes) => { + const shapes = slideAttributes.elements.map(element => { return convertElementToPptxShape(element); }).filter(Boolean); // Remove any null/undefined shapes @@ -80,9 +79,9 @@ export function convertElementAttributesToPptxSlides( }; // Add background color if available - if (backgroundColors && backgroundColors[index]) { + if (slideAttributes.backgroundColor) { slide.background = { - color: backgroundColors[index]!, + color: slideAttributes.backgroundColor, opacity: 1.0 }; }