From ff2977d2a01b87b6b1712984206fda1dfc004feb Mon Sep 17 00:00:00 2001 From: Suraj Jha Date: Fri, 1 Aug 2025 13:44:20 +0545 Subject: [PATCH] feat: add endpoints to save and retrieve generated layouts --- servers/fastapi/api/main.py | 2 + .../fastapi/api/v1/ppt/endpoints/prompts.py | 223 ++++++++++++++++++ servers/fastapi/api/v1/ppt/router.py | 3 +- .../models/sql/presentation_layout_code.py | 18 ++ 4 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 servers/fastapi/api/v1/ppt/endpoints/prompts.py create mode 100644 servers/fastapi/models/sql/presentation_layout_code.py diff --git a/servers/fastapi/api/main.py b/servers/fastapi/api/main.py index 93517b12..51eb2f02 100644 --- a/servers/fastapi/api/main.py +++ b/servers/fastapi/api/main.py @@ -5,6 +5,8 @@ from api.lifespan import app_lifespan from api.middlewares import UserConfigEnvUpdateMiddleware from api.v1.ppt.router import API_V1_PPT_ROUTER from utils.asset_directory_utils import get_exports_directory, get_images_directory, get_uploads_directory +# Import models to ensure they are registered with SQLModel +from models.sql.presentation_layout_code import PresentationLayoutCodeModel app = FastAPI(lifespan=app_lifespan) diff --git a/servers/fastapi/api/v1/ppt/endpoints/prompts.py b/servers/fastapi/api/v1/ppt/endpoints/prompts.py new file mode 100644 index 00000000..4ae51171 --- /dev/null +++ b/servers/fastapi/api/v1/ppt/endpoints/prompts.py @@ -0,0 +1,223 @@ + + + +GENERATE_HTML_SYSTEM_PROMPT = """ +You need to generate html and tailwind code for given presentation slide image. You need to think through each design elements and then decide where each element should go. +Follow these rules strictly: +- Make sure the design from html and tailwind is exact to the slide. +- Make sure all components are in their own place. +- Make sure size of elements are exact. +- Smallest of elements should be noted of and should be added as it is. +- Image's and icons's size and position should be added exactly as it is. +- Read through the OXML data of slide and then match exact position ans size of elements. Make sure to convert between dimension and pixels. +- Properly export shapes as exact SVG. +- Add relevant font in tailwind to all texts. +- Wrap the output code inside these classes: \"relative w-full rounded-sm max-w-[1280px] shadow-lg max-h-[720px] aspect-video bg-white relative z-20 mx-auto overflow-hidden\". For all images use this https://images.pexels.com/photos/31995895/pexels-photo-31995895/free-photo-of-turkish-coffee-with-scenic-bursa-view.jpeg url. +- Give out only HTML and Tailwind code. No other texts or explanations. + """ + +HTML_TO_REACT_SYSTEM_PROMPT = """ +Convert given static HTML and Tailwind slide to a TSX React component so that it can be dynamically populated. Follow these rules strictly while converting: + +1) Required imports, a zod schema and HTML layout has to be generated. +2) Schema will populate the layout so make sure schema has fields for all text, images and icons in the layout. +3) For similar components in the layouts (eg, team members), they should be represented by array of such components in the schema. +4) For image and icons icons should be a different schema with two dunder fields for prompt and url separately. +5) Default value for schema fields should be populated with the respective static value in HTML input. +6) In schema max and min value for characters in string and items in array should be specified as per the given image of the slide. You should accurately evaluate the maximum and minimum possible characters respective fields can handle visually through the image. +7) For image and icons schema should be compulsorily declared with two dunder fields for prompt and url separately. +8) Layout Id, layout name and layout description should be declared and should describe the structure of the layout not its purpose. Do not describe numbers of any items in the layout. + -Description should not have any purpose for elements in it, so use 'cards' instead of 'goal cards' and 'bullet points' instead of 'solution bullet points'. + -layoutName constant should be same as the component name in the layout. + -Layout Id examples: header-description-bullet-points-slide, header-description-image-slide + -Layout Name examples: HeaderDescriptionBulletPointsLayout, HeaderDescriptionImageLayout + -Layout Description examples: A slide with a header, description, and bullet points and A slide with a header, description, and image + +For example: +Input:

Effects of Global Warming

global warming effects on earth

Global warming triggers a cascade of effects on our planet. These changes impact everything from our oceans to our ecosystems.

sea level rising icon

Rising Sea Levels

Rising sea levels threaten coastal communities and ecosystems due to melting glaciers and thermal expansion.

heatwave icon

Intense Heatwaves

Heatwaves are becoming more frequent and intense, posing significant risks to human health and agriculture.

precipitation changes icon

Changes in Precipitation

Altered precipitation patterns lead to increased droughts in some regions and severe flooding in others, affecting water resources.

+Output: import React from 'react' +import * as z from "zod"; + +const ImageSchema = z.object({ + __image_url__: z.url().meta({ + description: "URL to image", + }), + __image_prompt__: z.string().meta({ + description: "Prompt used to generate the image", + }).min(10).max(50), +}) + +const IconSchema = z.object({ + __icon_url__: z.string().meta({ + description: "URL to icon", + }), + __icon_query__: z.string().meta({ + description: "Query used to search the icon", + }).min(5).max(20), +}) +export const layoutId = 'bullet-with-icons-slide' +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(40).default('Problem').meta({ + description: "Main title of the slide", + }), + description: z.string().max(150).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({ + __image_url__: 'https://images.unsplash.com/photo-1552664730-d307ca884978?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80', + __image_prompt__: 'Business people analyzing documents and charts in office' + }).meta({ + description: "Supporting image for the slide", + }), + bulletPoints: z.array(z.object({ + title: z.string().min(2).max(80).meta({ + description: "Bullet point title", + }), + description: z.string().min(10).max(150).meta({ + description: "Bullet point description", + }), + icon: IconSchema, + })).min(1).max(3).default([ + { + title: 'Inefficiency', + description: 'Businesses struggle to find digital tools that meet their needs, causing operational slowdowns.', + icon: { + __icon_url__: '/static/icons/placeholder.png', + __icon_query__: 'warning alert inefficiency' + } + }, + { + title: 'High Costs', + description: 'Outdated systems increase expenses, while small businesses struggle to expand their market reach.', + icon: { + __icon_url__: '/static/icons/placeholder.png', + __icon_query__: 'trending up costs chart' + } + } + ]).meta({ + description: "List of bullet points with icons and descriptions", + }) +}) + +export const Schema = bulletWithIconsSlideSchema + +export type BulletWithIconsSlideData = z.infer + +interface BulletWithIconsSlideLayoutProps { + data?: Partial +} + +const BulletWithIconsSlideLayout: React.FC = ({ data: slideData }) => { + const bulletPoints = slideData?.bulletPoints || [] + + return ( + <> + {/* Import Google Fonts */} + + +
+ + + {/* Main Content */} +
+ {/* Title Section - Full Width */} +
+

+ {slideData?.title || 'Problem'} +

+
+ + {/* Content Container */} +
+ {/* Left Section - Image with Grid Pattern */} +
+ {/* Grid Pattern Background */} +
+ + + + + + + + +
+ + {/* Image Container */} +
+
+ {slideData?.image?.__image_prompt__ +
+
+ + {/* Decorative Sparkle */} +
+ + + +
+
+ + {/* Right Section - Content */} +
+ {/* Description */} +

+ {slideData?.description || 'Businesses face challenges with outdated technology and rising costs, limiting efficiency and growth in competitive markets.'} +

+ + {/* Bullet Points */} +
+ {bulletPoints.map((bullet, index) => ( +
+ {/* Icon */} +
+ {bullet.icon.__icon_query__} +
+ + {/* Content */} +
+

+ {bullet.title} +

+
+

+ {bullet.description} +

+
+
+ ))} +
+
+
+
+
+ + ) +} + +export default BulletWithIconsSlideLayout +""" + +HTML_EDIT_SYSTEM_PROMPT = """ +You need to edit given html with respect to the indication and sketch in the given UI. You'll be given the code for current UI which is in presentation size, along with its visualization in image form. Over that you'll also be given another image which has indications of what might change in form of sketch in the UI. You will have to return the edited html with tailwind with the changes as indicated on the image and through prompt. Make sure you think through the design before making the change and also make sure you don't change the non-indicated part. Try to follow the design style of current content for generated content. If sketch image is not provided, then you need to edit the html with respect to the prompt. Only give out code and nothing else. +""" + diff --git a/servers/fastapi/api/v1/ppt/router.py b/servers/fastapi/api/v1/ppt/router.py index ba17f688..b2912026 100644 --- a/servers/fastapi/api/v1/ppt/router.py +++ b/servers/fastapi/api/v1/ppt/router.py @@ -9,7 +9,7 @@ from api.v1.ppt.endpoints.outlines import OUTLINES_ROUTER from api.v1.ppt.endpoints.presentation import PRESENTATION_ROUTER from api.v1.ppt.endpoints.pptx_slides import PPTX_SLIDES_ROUTER from api.v1.ppt.endpoints.slide import SLIDE_ROUTER -from api.v1.ppt.endpoints.slide_to_html import SLIDE_TO_HTML_ROUTER, HTML_TO_REACT_ROUTER, HTML_EDIT_ROUTER +from api.v1.ppt.endpoints.slide_to_html import SLIDE_TO_HTML_ROUTER, HTML_TO_REACT_ROUTER, HTML_EDIT_ROUTER, LAYOUT_MANAGEMENT_ROUTER API_V1_PPT_ROUTER = APIRouter(prefix="/api/v1/ppt") @@ -22,6 +22,7 @@ API_V1_PPT_ROUTER.include_router(SLIDE_ROUTER) API_V1_PPT_ROUTER.include_router(SLIDE_TO_HTML_ROUTER) API_V1_PPT_ROUTER.include_router(HTML_TO_REACT_ROUTER) API_V1_PPT_ROUTER.include_router(HTML_EDIT_ROUTER) +API_V1_PPT_ROUTER.include_router(LAYOUT_MANAGEMENT_ROUTER) API_V1_PPT_ROUTER.include_router(IMAGES_ROUTER) API_V1_PPT_ROUTER.include_router(ICONS_ROUTER) API_V1_PPT_ROUTER.include_router(OLLAMA_ROUTER) diff --git a/servers/fastapi/models/sql/presentation_layout_code.py b/servers/fastapi/models/sql/presentation_layout_code.py new file mode 100644 index 00000000..238bacde --- /dev/null +++ b/servers/fastapi/models/sql/presentation_layout_code.py @@ -0,0 +1,18 @@ +from datetime import datetime +from typing import Optional +from sqlalchemy import Column, DateTime, Text +from sqlmodel import SQLModel, Field + + +class PresentationLayoutCodeModel(SQLModel, table=True): + """Model for storing presentation layout codes""" + + __tablename__ = "presentation_layout_codes" + + id: Optional[int] = Field(default=None, primary_key=True) + presentation_id: str = Field(index=True, description="UUID of the presentation") + layout_id: str = Field(description="Unique identifier for the layout") + layout_name: str = Field(description="Display name of the layout") + layout_code: str = Field(sa_column=Column(Text), description="TSX/React component code for the layout") + created_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now)) + updated_at: datetime = Field(sa_column=Column(DateTime, default=datetime.now, onupdate=datetime.now)) \ No newline at end of file