Spaces:
Running
title: Powerpoint AI
emoji: 🎨
colorFrom: green
colorTo: yellow
sdk: docker
app_port: 7860
pinned: true
Powerpoint AI
Powerpoint AI is a Next.js presentation generator that turns a prompt into editable slides, lets the user refine them in a Google Slides-like editor, and exports the final deck as a .pptx file. The project is built around Hugging Face login and inference, a template-driven slide system, and a hybrid editor that supports both generated slide specs and draggable canvas elements.
This README is written as a learning guide. It explains:
- What the app does end-to-end.
- How to run it locally and deploy it to a Hugging Face Space.
- How the request flow moves from the homepage to the editor.
- What each important file does and how the files connect.
- How to rebuild the project manually if you want to learn by coding it yourself.
What The App Does
At a high level, the app works like this:
- The user opens
/and enters a presentation prompt. - The home page verifies that the user is logged in with Hugging Face.
- The prompt and selected template are stored in browser session/local storage.
- The app routes the user to
/editor. - The editor calls
/api/presentations/generatewith the prompt and model. - The backend requests structured JSON slides from Hugging Face Inference.
- The response is normalized into the app's
SlideSpecshape. - The editor renders those slides through the template/theme system.
- The user edits text, layout, images, and formatting inside the editor.
- The user exports the deck as an editable PowerPoint file.
Stack
- Next.js App Router
- React + TypeScript
- Tailwind CSS
- Hugging Face OAuth + Inference
- Unsplash image search/download integration
pptxgenjsfor editable PPTX export
Prerequisites
You need:
- Node.js 20+
- npm
- A Hugging Face account
- A manual Hugging Face connected OAuth app
- Optional: an Unsplash access key if you want real image search results
Environment Variables
Set these in .env for local development or in Hugging Face Space secrets/variables for deployment.
| Variable | Required | Purpose |
|---|---|---|
HUGGINGFACE_CLIENT_ID |
Yes for login | Client ID from your manual Hugging Face connected app |
HF_TOKEN |
Optional | Fallback Hugging Face token if the user is not logged in |
HF_API_KEY |
Optional | Alternate fallback token name supported by the generation route |
UNSPLASH_ACCESS_KEY |
Optional | Enables live Unsplash image search/results |
NEXT_PUBLIC_UNSPLASH_ACCESS_KEY |
Optional | Alternate client-visible Unsplash key fallback used by some routes |
Notes:
- This project is currently set up for a manual connected app, not Hugging Face Spaces native OAuth.
- Do not add
hf_oauth: trueif you want to keep using your existing connected app. - The allowed redirect URI in your Hugging Face connected app should be your actual app origin, for example:
http://localhost:3000/https://reubencf-powerpoint-ai.hf.space/
Local Development
Install dependencies:
npm installCreate
.envand add the variables you need:HUGGINGFACE_CLIENT_ID=your_client_id HF_TOKEN=your_optional_fallback_token UNSPLASH_ACCESS_KEY=your_optional_unsplash_keyStart the dev server:
npm run devOpen:
http://localhost:3000Log in with Hugging Face, enter a prompt, and verify that generation reaches
/editor.
Hugging Face Space Deployment
This repo is configured as a Docker Space using the frontmatter at the top of this file.
To deploy:
- Create a Hugging Face Docker Space.
- Push this repo to the Space.
- Add the required Space secrets/variables:
HUGGINGFACE_CLIENT_ID- optionally
HF_TOKEN - optionally
UNSPLASH_ACCESS_KEY
- In your manual connected Hugging Face app settings, add the Space origin as an allowed redirect URI.
- Redeploy the Space.
Important:
- The app currently uses
HUGGINGFACE_CLIENT_IDthrough/api/auth/client-id. - The login flow dynamically uses
window.location.originas the redirect target. - On Hugging Face Spaces, the homepage detects iframe/Space hosting and escapes the container before login if needed.
End-To-End Architecture Flow
1. Route entrypoints
/loads the homepage prompt builder./editorloads the editor only if a generation session was created from the homepage./templateexists as a template preview/browsing route./api/*provides auth, generation, AI edit, image search, and image proxy services.
2. Homepage to editor handoff
The homepage stores these keys before routing to the editor:
generationPromptgenerationModelisGeneratingeditorAccessppt_themein local storage
The editor reads those values on mount to decide whether to:
- redirect back to
/ - restore the chosen theme
- call the generation API automatically
3. Generation backend flow
/api/presentations/generate does the following:
- Validates the prompt.
- Resolves the Hugging Face token from headers, cookies, or env fallback.
- Builds the system prompt with
lib/slide-prompt.ts. - Calls the approved model through
lib/hf-client.ts. - Parses loose model JSON defensively.
- Normalizes the payload into the app's slide shape.
- Optionally enriches image slides with Unsplash data.
- Returns normalized slide data for the editor.
4. Editor rendering flow
The editor supports two rendering modes:
- Template mode: uses
SlideSpecandSlideFactoryto render theme-specific layouts. - Canvas mode: uses positioned
EditorElementobjects for draggable/editable items.
For generated presentations, the app first normalizes API data into SlideSpec, then builds canvas/editor elements where needed so the legacy editing features still work.
5. Export flow
- The top bar export button calls
hooks/useExport.ts. useExportdelegates tolib/editable-pptx-export.ts.- The exporter converts the current editor state into an editable
.pptx.
How The Editor Works Internally
The editor is intentionally state-heavy because it coordinates:
- the list of slides
- the current slide index
- selected element state
- inline text editing state
- zoom state
- undo/redo history
- AI text editing
- image replacement/search
- template-rendered slides and older element-based slides
The two most important ideas are:
SlideSpecis the declarative template-friendly shape.SlideModel/EditorElementis the interactive editor canvas shape.
That split is why some code looks duplicated: the app preserves a template rendering pipeline while also keeping direct canvas editing features.
How Templates And Themes Are Organized
There are three template families right now:
neobrutalismgalerynnoisy
Each template has:
- a declarative definition in
data/templates/*.ts - theme-specific React layouts in
components/slides/<theme>/layouts.tsx - fallback generic layout components in
components/slides/*.tsx
The registry in data/templates/index.ts describes:
- template IDs
- layout IDs
- field definitions
- style tokens
- default slide data
The SlideFactory uses that registry plus the selected template ID to render the correct slide component.
How AI Text Editing, Image Search, And Export Connect
AI text editing
- The editor opens
components/editor/AIToolsDialog.tsx. - That dialog talks to
/api/ai-edit-text/route.ts. - The route uses the Hugging Face provider layer to rewrite selected text.
Image search
- The editor opens
components/UnsplashImageSearch.tsx. - That component calls
/api/search-images/route.ts. /api/unsplash-download/route.tsis used for download attribution./api/image-proxy/route.tshelps keep remote images safe for capture/export flows.
Export
- The editor export button calls
hooks/useExport.ts. useExportcallslib/editable-pptx-export.ts.- The export library reads current slides, theme data, and layout information to create a real PowerPoint file.
File Map
This section is intentionally explicit so a new contributor can jump from route to component to helper without guessing.
App routes and app shell
app/layout.tsx- Root HTML/layout wrapper.
- Adds global fonts, metadata, and the
ThemeProvider.
app/globals.css- Global styles and utility-level CSS used across pages/components.
app/page.tsx- Root route entrypoint.
- Re-exports
components/home/HomePage.tsx.
app/editor/page.tsx- Guards
/editorwithsessionStorage.editorAccess. - Renders
GoogleSlidesEditor.
- Guards
app/template/page.tsx- Template preview/browsing route.
API routes
app/api/presentations/generate/route.ts- Main slide generation endpoint.
- Calls
HFClient,buildSlidePrompt, and Unsplash helpers.
app/api/ai-edit-text/route.ts- AI text rewriting endpoint for the editor.
app/api/search-images/route.ts- Searches Unsplash images for the editor modal.
app/api/unsplash-download/route.ts- Handles Unsplash download tracking.
app/api/image-proxy/route.ts- Proxies remote images for safe browser capture/export use.
app/api/auth/client-id/route.ts- Returns
HUGGINGFACE_CLIENT_IDto the client login flow.
- Returns
Homepage and top-level UI
components/home/HomePage.tsx- Landing page.
- Restores auth, handles login, stores generation state, and routes to
/editor.
components/ThemeProvider.tsx- Theme context wrapper built on
next-themes.
- Theme context wrapper built on
components/UnsplashImageSearch.tsx- Search/select modal for remote images.
Editor components
components/editor/GoogleSlidesEditor.tsx- Main workspace.
- Owns generation bootstrap, editor state, slide switching, toolbar wiring, and export entry.
components/editor/BottomToolbar.tsx- Floating toolbar for layout, typography, AI tools, zoom, and slide actions.
components/editor/AIToolsDialog.tsx- Text-editing dialog for AI-powered text changes.
Slide rendering
components/slides/SlideFactory.tsx- Central dispatcher that chooses the correct themed or fallback slide layout.
components/slides/TitleSlideLayout.tsx- Generic fallback layout for title/subtitle slides.
components/slides/AgendaSlideLayout.tsx- Generic fallback layout for agenda slides.
components/slides/TitleAndBodyLayout.tsx- Generic fallback layout for title/body slides.
components/slides/ThreeColumnLayout.tsx- Generic fallback layout for three-column slides.
components/slides/ImageAndTextLayout.tsx- Generic fallback layout for image/text slides.
components/slides/ReferenceLayout.tsx- Generic fallback layout for reference/list slides.
components/slides/ThankYouLayout.tsx- Generic fallback layout for ending slides.
components/slides/neobrutalism/layouts.tsx- Theme-specific layouts for the Neo-Brutalism template.
components/slides/galeryn/layouts.tsx- Theme-specific layouts for the Galeryn template.
components/slides/noisy/layouts.tsx- Theme-specific layouts for the Noisy template.
components/slides/shared/PersistedDraggableSurface.tsx- Shared editable/draggable slide-surface logic used by themed layouts.
Template registry
data/templates/index.ts- Template registry and template-related type definitions.
data/templates/neo-brutalism.ts- Neo-Brutalism template declaration.
data/templates/galeryn.ts- Galeryn template declaration.
data/templates/noisy.ts- Noisy template declaration.
Hooks
hooks/useExport.ts- Small export wrapper used by the editor.
hooks/useKeyboardShortcuts.ts- Keyboard shortcut behavior for editor actions.
hooks/useSlideHistory.ts- Undo/redo history storage for slides.
Library helpers
lib/ai-models.ts- Approved model constants and model validation.
lib/hf-client.ts- Hugging Face inference wrapper and provider-specific error handling.
lib/slide-prompt.ts- System prompt builder and layout normalization helpers.
lib/generated-presentation.ts- Typed generated-slide response helpers and
SlideSpecmapping.
- Typed generated-slide response helpers and
lib/template-options.ts- Template picker labels and normalization of saved IDs.
lib/theme-system.ts- Theme-level types/helpers used by the rendering system.
lib/editor-types.ts- Shared editor model types.
lib/editor-themes.ts- Theme/color/font definitions used by the editor and export code.
lib/layout-templates.ts- Default canvas layout creation helpers.
lib/editable-pptx-export.ts- Editable PowerPoint export implementation.
lib/capture-element.ts- DOM capture helper used by image/export flows.
lib/utils.ts- Small shared utility helpers.
Shared types
types/index.ts- Project-wide shared types that do not belong to a single feature module.
Config
next.config.ts- Next.js configuration.
package.json- Scripts, dependencies, and project metadata.
Dockerfile- Runtime image used by the Hugging Face Docker Space.
How To Manually Build This Project Yourself
If you want to learn by recreating the app manually, build it in this order:
Step 1: Create the app shell
- Start a Next.js app with TypeScript and App Router.
- Add
app/layout.tsxandapp/globals.css. - Add a
ThemeProviderso dark/light mode is available globally.
Step 2: Build the homepage
- Create a landing page with:
- a prompt textarea
- a template picker
- a submit button
- Add client-side storage for:
- the prompt
- the selected template
- a session flag that allows
/editor
- Add Hugging Face login/logout state restoration.
Step 3: Add Hugging Face login
- Create a manual connected app in Hugging Face settings.
- Add
HUGGINGFACE_CLIENT_IDto env/secrets. - Create
/api/auth/client-id/route.ts. - Use
oauthLoginUrlandoauthHandleRedirectIfPresenton the homepage. - Store the returned access token in local storage for later API calls.
Step 4: Create the generation API
- Create
/api/presentations/generate/route.ts. - Accept a prompt and optional model input.
- Resolve the Hugging Face token from headers/cookies/env.
- Build a strict system prompt that asks for presentation JSON.
- Call the model through a small Hugging Face client wrapper.
- Parse and normalize the model response into your own slide format.
Step 5: Define the slide data model
- Create a template-side shape like
SlideSpec. - Create an editor-side shape like
SlideModel/EditorElement. - Add normalization helpers that map generated API data into those shapes.
Step 6: Build the template system
- Define templates in
data/templates/*.ts. - Add a registry in
data/templates/index.ts. - Create a
SlideFactorythat renders a slide by template ID + layout ID. - Add generic fallback layouts so rendering still works if a template-specific layout is missing.
Step 7: Build the editor
- Create an editor page that reads the generation session.
- Call the generation API on first load.
- Store slides, current selection, zoom, and undo/redo history.
- Render:
- a slide thumbnail rail
- a main canvas
- a toolbar
- dialogs/modals for AI editing and image search
Step 8: Add AI text editing and images
- Create
/api/ai-edit-text/route.ts. - Add a dialog to send selected text for rewrite.
- Create Unsplash search and download routes.
- Add an image picker modal in the editor.
Step 9: Add export
- Create a hook for export actions.
- Convert the current slide state into
pptxgenjscalls. - Expose a top-bar export button in the editor.
Step 10: Polish and verify
- Add keyboard shortcuts and history.
- Add route guards so
/editoris only reached through the expected flow. - Verify login, generation, editing, image selection, and export.
Contributor Notes
- Prefer the template registry and
SlideSpecpath when adding new slide styles. - Keep behavior changes separate from cleanup/documentation changes.
- If you change storage keys or generation payload shapes, update both the homepage/editor handoff and this README.
- If you add a new route or editor subsystem, add it to the file map above so new contributors can follow the flow quickly.