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
};
}