Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions servers/fastapi/api/v1/ppt/endpoints/presentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ async def inner():
asyncio.create_task(process_slide_and_fetch_assets(image_generation_service, slide))
)

if i > 0:
yield SSEResponse(
event="response",
data=json.dumps({"type": "chunk", "chunk": ","}),
).to_string()

yield SSEResponse(
event="response",
data=json.dumps({"type": "chunk", "chunk": slide.model_dump_json()}),
Expand Down
60 changes: 60 additions & 0 deletions servers/fastapi/test_layout_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Quick test of the layout generation pipeline."""
import asyncio
import sys
import os
import traceback

# Ensure the package is on the path
sys.path.insert(0, os.path.dirname(__file__))

from models.presentation_outline_model import SlideOutlineModel
from utils.llm_calls.generate_slide_layouts import (
categorize_outlines,
generate_layout_tsx,
generate_all_layouts,
LAYOUT_TYPES,
)

OUTLINES = [
SlideOutlineModel(content="Title: Benefits of Renewable Energy - An overview of why renewable energy matters"),
SlideOutlineModel(content="Solar Power: Cost savings, environmental benefits, and growing adoption worldwide"),
SlideOutlineModel(content="Wind Energy: Offshore and onshore wind farms generating clean electricity"),
]

async def test_categorize():
print("=== Testing categorize_outlines ===")
try:
result = await categorize_outlines(OUTLINES, "Benefits of Renewable Energy")
print(f"Result: {result}")
return result
except Exception as e:
print(f"ERROR: {e}")
traceback.print_exc()
return None

async def test_generate_tsx():
print("\n=== Testing generate_layout_tsx ===")
try:
tsx = await generate_layout_tsx(
LAYOUT_TYPES[0], # title-intro
"Benefits of Renewable Energy",
"Title: Benefits of Renewable Energy",
)
print(f"Generated TSX ({len(tsx)} chars):")
print(tsx[:500])
return tsx
except Exception as e:
print(f"ERROR: {e}")
traceback.print_exc()
return None

async def main():
cat_result = await test_categorize()
tsx_result = await test_generate_tsx()

if cat_result and tsx_result:
print("\n=== Both tests passed ===")
else:
print("\n=== Some tests FAILED ===")

asyncio.run(main())
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client";
import React, { useEffect, useState } from "react";

interface StreamingProgressBarProps {
streamed: number;
total: number;
}

const StreamingProgressBar: React.FC<StreamingProgressBarProps> = ({
streamed,
total,
}) => {
const [visible, setVisible] = useState(false);

useEffect(() => {
// Slight delay so it fades in gracefully
const t = setTimeout(() => setVisible(true), 50);
return () => clearTimeout(t);
}, []);

const pct = total > 0 ? Math.round((streamed / total) * 100) : 0;

return (
<div
className={`sticky bottom-4 z-50 mx-auto mb-2 w-full max-w-[520px] transition-opacity duration-500 ${
visible ? "opacity-100" : "opacity-0"
}`}
>
<div className="flex flex-col gap-2 rounded-2xl border border-[#E8E4FF] bg-white/90 px-5 py-3 shadow-xl backdrop-blur-sm">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{/* Pulsing dot */}
<span className="relative flex h-2.5 w-2.5">
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#5141e5] opacity-60" />
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-[#5141e5]" />
</span>
<span className="text-sm font-medium text-gray-700">
Generating presentation…
</span>
</div>
<span className="text-xs font-semibold text-[#5141e5]">
{streamed}/{total} slides
</span>
</div>

<div className="h-1.5 w-full overflow-hidden rounded-full bg-gray-100">
<div
className="h-full rounded-full bg-gradient-to-r from-[#9034EA] to-[#5141e5] transition-all duration-500 ease-out"
style={{ width: `${pct}%` }}
/>
</div>
</div>
</div>
);
};

export default StreamingProgressBar;
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,15 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
>
{/* Icon */}
<div style={{ background: "var(--primary-color,#9333ea)" }} className="flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text, #ffffff)"
title={bullet.icon.__icon_query__}
/>
{bullet.icon?.__icon_url__ && (
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text, #ffffff)"
title={bullet.icon.__icon_query__}
/>
)}
</div>

{/* Content */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,15 @@ const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({
<div key={index} className="flex items-start space-x-4">
{/* Icon */}
<div style={{ background: "var(--primary-color,#9333ea)" }} className="flex-shrink-0 w-12 h-12 rounded-lg shadow-md flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text, #ffffff)"
title={bullet.icon.__icon_query__}
/>
{bullet.icon?.__icon_url__ && (
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text, #ffffff)"
title={bullet.icon.__icon_query__}
/>
)}
</div>

{/* Content */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,15 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
{/* Icon and Title */}
<div className="flex items-center space-x-3 mb-3">
<div style={{ background: "var(--primary-color,#9333ea)" }} className="w-8 h-8 rounded-lg flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-5 h-5"
color="var(--primary-text, #ffffff)"
title={bullet.icon.__icon_query__}
/>
{bullet.icon?.__icon_url__ && (
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-5 h-5"
color="var(--primary-text, #ffffff)"
title={bullet.icon.__icon_query__}
/>
)}
</div>
<h3 style={{ color: "var(--primary-text, #ffffff)" }} className="text-lg font-semibold">
{bullet.title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,15 @@ const BulletIconsOnlySlideLayout: React.FC<BulletIconsOnlySlideLayoutProps> = ({
>
{/* Icon */}
<div style={{ background: "var(--primary-color,#9333ea)" }} className="flex-shrink-0 w-12 h-12 rounded-full flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text,#ffffff)"
title={bullet.icon.__icon_query__}
/>
{bullet.icon?.__icon_url__ && (
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text,#ffffff)"
title={bullet.icon.__icon_query__}
/>
)}
</div>

{/* Content */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,15 @@ const BulletWithIconsSlideLayout: React.FC<BulletWithIconsSlideLayoutProps> = ({
<div key={index} className="flex items-start space-x-4">
{/* Icon */}
<div style={{ background: "var(--primary-color,#9333ea)" }} className="flex-shrink-0 w-12 h-12 rounded-lg shadow-md flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text,#ffffff)"
title={bullet.icon.__icon_query__}
/>
{bullet.icon?.__icon_url__ && (
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text,#ffffff)"
title={bullet.icon.__icon_query__}
/>
)}
</div>

{/* Content */}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -553,13 +553,15 @@ const ChartWithBulletsSlideLayout: React.FC<ChartWithBulletsSlideLayoutProps> =
{/* Icon and Title */}
<div className="flex items-center space-x-3 mb-3">
<div style={{ background: "var(--primary-color,#9333ea)" }} className="w-8 h-8 rounded-lg flex items-center justify-center">
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-5 h-5"
color="var(--primary-text,#ffffff)"
title={bullet.icon.__icon_query__}
/>
{bullet.icon?.__icon_url__ && (
<RemoteSvgIcon
url={bullet.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-5 h-5"
color="var(--primary-text,#ffffff)"
title={bullet.icon.__icon_query__}
/>
)}
</div>
<h3 style={{ color: "var(--background-text,#ffffff)" }} className="text-lg font-semibold">
{bullet.title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,15 @@ const InfographicFourIcons: React.FC<SlideLayoutProps> = ({ data }) => {
style={{ backgroundColor: 'var(--primary-color, #BFF4FF)' }}
>
{/* Icon */}
<RemoteSvgIcon
url={item.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-14 h-14"
color="var(--primary-text, #111827)"
title={item.icon.__icon_query__}
/>
{item.icon?.__icon_url__ && (
<RemoteSvgIcon
url={item.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-14 h-14"
color="var(--primary-text, #111827)"
title={item.icon.__icon_query__}
/>
)}
</div>
</div>
<div className="mt-5 text-[16px] font-semibold" style={{ color: "var(--background-text, #111827)" }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,15 @@ const FeatureCards: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
<div key={i} className="rounded-[22px] shadow-[0_16px_40px_rgba(0,0,0,0.08)] overflow-hidden" style={{ backgroundColor: 'var(--primary-color, #BFF4FF)' }}>
<div className="px-6 py-5">
<div className="w-10 h-10 rounded-sm flex items-center justify-center" style={{ backgroundColor: 'var(--primary-color, #FFFFFF)' }}>
<RemoteSvgIcon
url={f.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text, #111827)"
title={f.icon.__icon_query__}
/>
{f.icon?.__icon_url__ && (
<RemoteSvgIcon
url={f.icon.__icon_url__}
strokeColor={"currentColor"}
className="w-6 h-6"
color="var(--primary-text, #111827)"
title={f.icon.__icon_query__}
/>
)}
</div>
<div className="mt-4 text-[18px] font-semibold whitespace-pre-line" style={{ color: 'var(--primary-text, #111827)' }}>{f.title}</div>
<p className="mt-3 text-[14px] leading-[1.7]" style={{ color: 'var(--primary-text, #6B7280)' }}>{f.body}</p>
Expand Down
4 changes: 3 additions & 1 deletion servers/nextjs/app/presentation-templates/swift/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,9 @@ const Timeline: React.FC<SlideLayoutProps> = ({ data: slideData }) => {
style={{ backgroundColor: 'var(--card-color, #FFFFFF)' }}
>
<div className="mx-auto w-12 h-12 rounded-full flex items-center justify-center mb-4" style={{ backgroundColor: 'var(--primary-color, #BFF4FF)' }}>
<img src={it.icon.__icon_url__} alt={it.icon.__icon_query__} className="w-6 h-6 object-contain" />
{it.icon?.__icon_url__ && (
<img src={it.icon.__icon_url__} alt={it.icon.__icon_query__} className="w-6 h-6 object-contain" />
)}
</div>
<div className="text-[18px] font-semibold" style={{ color: 'var(--background-text, #111827)' }}>{it.heading}</div>
<p className="mt-3 text-[14px]" style={{ color: 'var(--background-text, #6B7280)' }}>{it.body}</p>
Expand Down
Loading