diff --git a/electron/servers/fastapi/alembic/versions/82abdbc476a7_add_theme_column_to_presentations.py b/electron/servers/fastapi/alembic/versions/82abdbc476a7_add_theme_column_to_presentations.py new file mode 100644 index 00000000..c7282a6e --- /dev/null +++ b/electron/servers/fastapi/alembic/versions/82abdbc476a7_add_theme_column_to_presentations.py @@ -0,0 +1,31 @@ +"""add theme column to presentations + +Revision ID: 82abdbc476a7 +Revises: f42ad4074449 +Create Date: 2026-03-24 12:42:46.220359 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = '82abdbc476a7' +down_revision: Union[str, None] = 'f42ad4074449' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + pass + # ### end Alembic commands ### diff --git a/electron/servers/fastapi/alembic/versions/f42ad4074449_add_theme_column_to_presentations.py b/electron/servers/fastapi/alembic/versions/f42ad4074449_add_theme_column_to_presentations.py new file mode 100644 index 00000000..4cc8c46f --- /dev/null +++ b/electron/servers/fastapi/alembic/versions/f42ad4074449_add_theme_column_to_presentations.py @@ -0,0 +1,31 @@ +"""add theme column to presentations + +Revision ID: f42ad4074449 +Revises: 00b3c27a13bc +Create Date: 2026-03-24 12:42:32.369006 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = 'f42ad4074449' +down_revision: Union[str, None] = '00b3c27a13bc' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('presentations', sa.Column('theme', sa.JSON(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('presentations', 'theme') + # ### end Alembic commands ### diff --git a/electron/servers/fastapi/api/v1/ppt/endpoints/presentation.py b/electron/servers/fastapi/api/v1/ppt/endpoints/presentation.py index 27a01244..cb616c15 100644 --- a/electron/servers/fastapi/api/v1/ppt/endpoints/presentation.py +++ b/electron/servers/fastapi/api/v1/ppt/endpoints/presentation.py @@ -366,6 +366,7 @@ async def update_presentation( id: Annotated[uuid.UUID, Body()], n_slides: Annotated[Optional[int], Body()] = None, title: Annotated[Optional[str], Body()] = None, + theme: Annotated[Optional[dict], Body()] = None, slides: Annotated[Optional[List[SlideModel]], Body()] = None, sql_session: AsyncSession = Depends(get_async_session), ): @@ -378,10 +379,11 @@ async def update_presentation( presentation_update_dict["n_slides"] = n_slides if title: presentation_update_dict["title"] = title + if theme: + presentation_update_dict["theme"] = theme - if n_slides or title: + if presentation_update_dict: presentation.sqlmodel_update(presentation_update_dict) - if slides: # Just to make sure id is UUID for slide in slides: diff --git a/electron/servers/fastapi/api/v1/ppt/endpoints/theme.py b/electron/servers/fastapi/api/v1/ppt/endpoints/theme.py index 4ddd38d9..f597b82f 100644 --- a/electron/servers/fastapi/api/v1/ppt/endpoints/theme.py +++ b/electron/servers/fastapi/api/v1/ppt/endpoints/theme.py @@ -1,3 +1,4 @@ +import copy import uuid from typing import Any, List, Optional @@ -67,7 +68,9 @@ def _read_themes_from_row(row: Optional[KeyValueSqlModel]) -> list[dict[str, Any return [] value = row.value if isinstance(row.value, dict) else {} themes = value.get("themes", []) - return themes if isinstance(themes, list) else [] + if not isinstance(themes, list): + return [] + return copy.deepcopy(themes) async def _resolve_logo_url( diff --git a/electron/servers/fastapi/models/presentation_with_slides.py b/electron/servers/fastapi/models/presentation_with_slides.py index 3a4d83b4..f508f4a3 100644 --- a/electron/servers/fastapi/models/presentation_with_slides.py +++ b/electron/servers/fastapi/models/presentation_with_slides.py @@ -18,3 +18,4 @@ class PresentationWithSlides(BaseModel): tone: Optional[str] = None verbosity: Optional[str] = None slides: List[SlideModel] + theme: Optional[dict] = None diff --git a/electron/servers/fastapi/models/sql/presentation.py b/electron/servers/fastapi/models/sql/presentation.py index 050bf2f2..d2d57e6c 100644 --- a/electron/servers/fastapi/models/sql/presentation.py +++ b/electron/servers/fastapi/models/sql/presentation.py @@ -41,6 +41,7 @@ class PresentationModel(SQLModel, table=True): include_table_of_contents: bool = Field(sa_column=Column(Boolean), default=False) include_title_slide: bool = Field(sa_column=Column(Boolean), default=True) web_search: bool = Field(sa_column=Column(Boolean), default=False) + theme: Optional[dict] = Field(sa_column=Column(JSON), default=None) def get_new_presentation(self): return PresentationModel( diff --git a/electron/servers/fastapi/placeholder b/electron/servers/fastapi/placeholder index aa73729d..4ff509e6 100644 Binary files a/electron/servers/fastapi/placeholder and b/electron/servers/fastapi/placeholder differ diff --git a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx index 03fb3b7b..72bd5a04 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/PresentationHeader.tsx @@ -108,6 +108,7 @@ const PresentationHeader = ({ if (isStreaming) return; try { + toast.info("Exporting PPTX..."); setIsExporting(true); // Save the presentation data before exporting trackEvent(MixpanelEvent.Header_UpdatePresentationContent_API_Call); @@ -146,6 +147,7 @@ const PresentationHeader = ({ if (isStreaming) return; try { + toast.info("Exporting PDF..."); setIsExporting(true); // Save the presentation data before exporting trackEvent(MixpanelEvent.Header_UpdatePresentationContent_API_Call); @@ -250,7 +252,7 @@ const PresentationHeader = ({ {isPresentationSaving &&
} - +
diff --git a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx index a31f9bfc..5c58df1d 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx +++ b/electron/servers/nextjs/app/(presentation-generator)/presentation/components/ThemeSelector.tsx @@ -3,12 +3,11 @@ import React, { useState } from 'react' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { Palette } from 'lucide-react'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { updateTheme } from '@/store/slices/presentationGeneration'; import { useRouter } from 'next/navigation'; import { useFontLoader } from '../../hooks/useFontLoad'; -import { RootState } from '@/store/store'; -const ThemeSelector = ({ presentation_id, current_theme, themes: allThemes }: { presentation_id: string, current_theme: any, themes: any[] }) => { +const ThemeSelector = ({ current_theme, themes: allThemes }: { current_theme: any, themes: any[] }) => { const [currentTheme, setCurrentTheme] = useState(current_theme) const dispatch = useDispatch() const [isOpen, setIsOpen] = useState(false) diff --git a/electron/servers/nextjs/app/(presentation-generator)/services/api/types.ts b/electron/servers/nextjs/app/(presentation-generator)/services/api/types.ts index f1f45afd..ac5b6108 100644 --- a/electron/servers/nextjs/app/(presentation-generator)/services/api/types.ts +++ b/electron/servers/nextjs/app/(presentation-generator)/services/api/types.ts @@ -25,10 +25,10 @@ export interface DeplotResponse { } export interface ImageAssetResponse { - message: string; - path: string; - id: string; - file_url?: string; + message: string; + path: string; + id: string; + file_url?: string; }