Skip to content

Office Add-in Integration

The Office Add-in integration in pptcraft relies on a dynamic manifest generation system and a JavaScript bridge to communicate with the PowerPoint host. The manifest is rendered from a Jinja2 template with a persistent UUID to ensure PowerPoint pins the add-in state correctly without requiring re-sideloading on every restart 1. The JavaScript bridge, loaded into the taskpane, exposes methods to the backend and UI controller for slide manipulation, ensuring all interactions use opaque Slide.id strings rather than shifting indices 2.

The manifest is generated by src/ppt_craft/manifest.py, which renders src/ppt_craft/templates/manifest.xml.j2 1. A critical design choice is the persistence of the add-in’s Id under ~/.local/share/ppt-craft/install.id. Because PowerPoint pins state to the manifest Id, rotating it would force re-sideloading. The get_or_create_install_id() function ensures the UUID is created once and reused.

diagram

The bridge is implemented in src/ppt_craft/static/taskpane/officejs-bridge.js and loaded into the taskpane via src/ppt_craft/static/taskpane/index.html 3. The taskpane loads the hosted CDN version of office.js (https://appsforoffice.microsoft.com/lib/1/hosted/office.js). The bridge exposes a window.bridge object containing methods for slide operations 2.

The ready() function initializes the bridge, checking for Office.HostType.PowerPoint and PowerPointApi 1.8 support. The snapshotDeck() function retrieves the full presentation as a base64 string using getFileAsync with 4MB slices. Slide enumeration is handled by listSlides(), which returns an array of {id, title} objects using PowerPoint.run.

Slide insertion and replacement are managed by insertOrReplaceSlide(), which uses ctx.presentation.insertSlidesFromBase64(). If mode is “replace”, it deletes the target slide by ID after insertion. The openNewFromBase64() function creates a new presentation if the base64 string is under 71,680,000 characters; otherwise, it falls back to a client-side <a download> blob.

The taskpane UI is defined in src/ppt_craft/static/taskpane/index.html and controlled by src/ppt_craft/static/taskpane/app.js 3. The app.js module connects to the backend via WebSocket and uses window.bridge for all PowerPoint interactions 4.

The bootstrap() function in app.js calls window.bridge.ready() to ensure the host is PowerPoint and the API is supported. It then establishes a WebSocket connection to /api/ws/{SESSION_ID}. The onWsMessage() handler processes server events: slide_ready triggers window.bridge.insertOrReplaceSlide(), and deck_complete updates the UI status.

Manual actions in the UI, such as “List slides” or “Insert stub slide,” are wired to app.js event listeners that call corresponding window.bridge methods. For example, btn-insert-stub fetches a slide from the backend API and uses window.bridge.insertOrReplaceSlide() to add it to the presentation. The chat input sends user intent to the server via WebSocket, which then processes the request and pushes updates back to the taskpane.