Skip to content

feat(x): create and edit presentations from chat - #848

Merged
Gagancreates merged 25 commits into
rowboatlabs:mainfrom
prakhar1605:feat/deck-generation
Aug 20, 2026
Merged

feat(x): create and edit presentations from chat#848
Gagancreates merged 25 commits into
rowboatlabs:mainfrom
prakhar1605:feat/deck-generation

Conversation

@prakhar1605

@prakhar1605 prakhar1605 commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

You can now create and edit PowerPoint presentations by talking to the
assistant.

What it does

  • "Make me a pitch deck for my startup" — the assistant asks a few
    questions as clickable option cards (purpose, length, then audience and
    facts), then writes a real .pptx into the workspace and opens it in the
    editor.
  • With a deck open, "change slide 2's heading", "add a slide about
    pricing", "restyle this deck to midnight" all work — the assistant
    knows which deck you're looking at, and the open editor updates in
    place.
  • The assistant only uses facts you give it. Missing numbers become
    visible placeholders like "[X]% growth" instead of made-up values.
  • Files created in chat now open inside the app instead of in
    Keynote/PowerPoint.

How to test

Setup: pnpm install first — this adds jszip and fast-xml-parser to
packages/shared. Then npm run deps && npm run dev.

  1. Create from chat. In any chat: "make me a pitch deck for my
    startup". You should get question cards — pick the options, then
    answer the facts question. A .pptx appears in the workspace and opens
    in the editor. Try Skip on one card too: it should build with
    bracketed placeholders instead of asking again.
  2. Edit the open deck, without naming the file. "restyle this deck to
    midnight", then "make slide 2's heading shorter", then "add a slide
    about pricing". Each should hit the deck you're looking at.
  3. Use an existing deck. Open any .pptx you already have (one from
    Canva or PowerPoint is the better test). "make this deck better" —
    the assistant should look at what's there before suggesting changes.
    "shorten slide 3" should just do it.
  4. Your formatting survives. Bold a word by hand in the editor, wait
    for Saved, then ask the assistant to change a different paragraph on
    that slide. Your bold should still be there.
  5. Conflict handling. Type into a text box and don't wait for the
    save, then ask for a restyle in chat. A banner appears — try both
    Reload and Keep mine.
  6. Open the result in desktop PowerPoint. No repair prompt, text and
    theme intact. Worth doing on the edited existing deck too — untouched
    slides, charts and tables should be exactly as they were. This is the
    most important step.
  7. PDFs still work. "Make me a PDF handout of something" should still
    produce a PDF.

Changes worth knowing about

  • The pptx engine (parser, serializer, generation) moved from the
    renderer into packages/shared so the assistant's tools can use it.
    Moved with git mv, history preserved; jszip and fast-xml-parser
    added to shared's deps. No pnpm-workspace or tsconfig changes.
  • "Make a presentation" used to produce a PDF — the prompt routed it to
    the PDF pipeline, and the new deck tools weren't attached to any skill
    so the model never saw them. Presentations now go to the deck tools;
    the PDF pipeline still exists but only fires on an explicit PDF
    request. This changes behavior for anyone who was getting PDFs.
  • The AI buttons inside the editor (sparkle icons, the Generate dialog)
    are removed — chat is the AI surface now. Blank deck creation and all
    normal editor controls stay.
  • The intake questions use the ask-human question card from feat(x): interactive question card for ask-human #853.
  • CI: the package smoke test gets 6GB of node heap — the renderer build
    outgrew the runner default. Code-splitting the engine is the real fix.

Known limits

  • Charts, SmartArt and tables in existing decks show as placeholders;
    their data is preserved untouched on save.
  • The no-made-up-facts rule is enforced through the prompts, not code —
    a model can still slip. Real user-supplied numbers have to pass
    through, so a hard validator isn't possible.
  • If you have unsaved edits, the assistant changes the file, and you
    navigate away without choosing — your unsaved edits are lost (there's
    no UI left to ask at that point).
  • Windows and Linux testing pending.

Verified

Ran all seven steps end to end in the app on macOS. Test suites: shared
296, core 687, renderer 265 — all green, typecheck, eslint and builds
clean.

prakhar1605 and others added 19 commits August 7, 2026 23:28
Synthesizes a complete minimal OOXML package from strings — theme with
three built-in palettes, one master, two layouts, one title slide —
validated structurally and round-tripped through our own parser and
serializer. New presentation entry in the workspace Add menu opens the
created deck directly in the editor.
…nders them

The typed text was always in the written bytes - generated packages
validated against ECMA-376 and rendered in Keynote and QuickLook, but
desktop PowerPoint silently renders schema-minimal packages blank. The
synthesizer now emits PowerPoint's own scaffolding: presProps, viewProps,
tableStyles, the full Office theme matrix with font script tables,
nine-level master txStyles, a presentation defaultTextStyle, and bgRef
backgrounds. Scaffolding adapted from python-pptx's default template
(MIT), attributed in NOTICE.md.
Core generateDeckOutline turns a prompt into a zod-validated outline
(clarifying questions, slides with headings/bullets) following the
existing knowledge-task model pattern, exposed via deck:generateOutline
IPC. The dialog gains a Generate mode: prompt, optional clarify round,
editable outline review, then deterministic synthesis through
newDeckPptx + planNewSlide in one writeDeck call.
Shared FACT_RULES across all three generation prompts: only user-supplied
facts, bracketed placeholders for missing numbers/quotes, big-number and
quote patterns gated on real input, needsInput chips surfacing what the
user should fill in. Clarifying questions sized to the gap (2-5 typical,
cap 8) instead of a hard cap of 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the sparkle popover out of pptx-editor.tsx into its own module and
add the first tests for it, covering the Edit/New mode switch (the
'New slide after this' tab), Suggest/Generate/Apply routing, and the
tabless rail-header variant. Behaviour unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Gamma-style cards with drag reorder, per-card pattern switcher, inline
theme strip and staggered entrance; two-pane review where every card
carries a real rendered thumbnail and the selected slide previews large,
rebuilt in memory through the same synthesis path Create uses - nothing
touches disk until Create.
Eleven engine modules plus edit-slide relocate from the renderer to
shared (git mv, history preserved) so core can use them; renderer
deep-imports repoint following the existing @x/shared/dist convention.
Five new builtin tools - deck-create/add-slide/edit-slide/restyle/review
- mirror the spreadsheet tools pattern: file-boundary permission,
errorEnvelope, structured inputs, honesty contract in the descriptions.
writeBuffer ported byte-identical from the spreadsheet branch. Node
parse path skips blob URLs via a MediaSink; tools dispose decks in
finally.
Deck tools announce writes via a rowboat:deck-touched event mirroring
the spreadsheet pattern; the editor reloads in place when clean and
shows a Reload/Keep-mine banner when dirty. Every editor write path now
carries the snapshot's etag, verified under the main-process file lock,
so a stale editor can never silently overwrite an assistant's write.
Auto-open fires only on deck-create so an assistant edit never steals
the user's view.
The deck tools were in no skill and not in base tools, so the model never
saw them, while both prompt surfaces pointed presentations at the PDF
pipeline. A new create-presentations skill attaches all five deck tools;
the old PDF skill moves to pdf-slides, gated behind an explicit PDF ask.
Tool descriptions lead with the user's own vocabulary.
Drops the slide-rail sparkles and the dialog's Generate mode (prompt,
clarify, outline review, live previews) now that the deck tools work
from chat. Blank creation, the theme popover and every non-AI editor
control stay; shared/core machinery is untouched since the tools depend
on it.
Adds a deck kind to the middlePane user-context union, so every message
carries the open deck's path and slide position. The agent now targets
the open deck for 'this deck' / 'slide 2' instead of guessing or
creating a new file; deck-create is explicitly guarded against
edit-like requests. Follows the existing note/browser context pattern -
no new IPC, no core-to-renderer query, no capability changes, and the
prompt snapshots gain only the new paragraph.
Assistant-created decks opened in Keynote/PowerPoint because the file
card always called shell:openPath. Workspace files with an in-app viewer
now route through onOpenKnowledgeFile, using the same getViewerType
predicate that mounts the viewers so the card and the router can't
drift. Files outside the workspace and types with no viewer keep the OS
opener.
New decks get a single compact intake - purpose and audience, length,
and the facts the deck depends on - offered as answerable options, plus
deck-type slide arcs so the outline follows a known structure. Editing
an open deck gets a lighter path: review first, at most one clarifying
question, and never a full regenerate.
@prakhar1605 prakhar1605 changed the title feat(x): chat-driven deck creation and editing — pptx engine in shared + five agent tools feat(x): create and edit presentations from chat Aug 12, 2026
…ntake

Inline emphasis from the model becomes real bold/italic runs instead of
literal asterisks on the slide. Editing a slide now diffs paragraphs, so
formatting the user applied by hand survives on paragraphs the edit
didn't touch. deck-create requires purpose, audience and length in its
schema, so a new deck can't be built without asking first.
The renderer bundle now includes the shared pptx engine; rollup's chunk-rendering phase crossed the runner's default heap and the vite build died with heap out of memory. Local builds pass because node sizes its heap from full machine memory. 6GB headroom on a 16GB runner.
@prakhar1605
prakhar1605 requested a review from arkml August 13, 2026 10:51
The intake questions now come as clickable cards (rowboatlabs#853) instead of
prose: purpose and length as single-select options, audience and facts
as one open question after purpose is known. Skip follows the card's
sentinel and proceeds with placeholders rather than re-asking. At most
two cards per message, two rounds total.
@Gagancreates

Gagancreates commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Reviewed the branch locally — great feature overall. One thing I hit while testing, plus a suggestions list from a deep review pass.

Observed while testing: assistant edits are not visible live — with the deck open in the editor, chat edits land on disk but the editor doesn't refresh to show them (may be related to items 1–2 below, since the reload path only fires when the pipeline is idle and classifies the change as external).

Correctness — should fix before merge

  • Deck tools clobber concurrent editor savespackages/core/src/runtime/tools/domains/deck.ts (~243): deck-add-slide / deck-edit-slide / deck-restyle do read → modify → files.writeBuffer with no expectedEtag. A user edit saved between the tool's read and write is silently overwritten, and the editor then reloads the clobbered bytes with no conflict banner. Fix: thread the etag from the read through to the write and surface a conflict on mismatch.
  • reloadFromDisk adopts the new etag before backing outapps/renderer/src/components/pptx-editor.tsx (~441): if the user starts editing during the async load, the function backs out and shows the banner, but the snapshot etag was already updated — so the armed autosave passes the guard and overwrites the assistant's write. Fix: only adopt the loaded etag after the idle check passes.
  • Deleting the open deck breaks saving foreverpackages/core/src/filesystem/files.ts (~456): the etag guard lstats the missing file and throws raw ENOENT, which doesn't match file-sync's 'ETag mismatch' check — endless error toasts, edits can never persist. (Contradicts the comment at pptx-editor.tsx:531 that the next save recreates the file.) Fix: treat missing-file as either "recreate" or a proper conflict.
  • slotsFor('bullets') silently drops bullet editspackages/shared/src/pptx/edit-slide.ts (~161): on a slide with a single text shape, edited bullets have no slot, planSlideEdit returns noop, and the tool reports success with changed:false. Fix: return null to force a replace when the bullets slot can't be built.
  • Prompts promise slide delete/reorder but no tool exposes themdeck.ts (~150) + create-presentations/skill.ts:99: "cut the market slide" / "reorder slides" are unservable — writeDeck already supports deleteSlides/slideOrder, only the tool surface is missing. Fix: add the params to an existing tool (or a small deck-restructure tool).
  • changeTheme mutates in-memory theme before persistingpptx-editor.tsx (~650): if persistFullDeck throws (e.g. guarded-write conflict), no history entry is pushed — the new theme is on screen, old bytes on disk, and Cmd+Z can't undo it.
  • Theme undo/redo IIFE has no catchpptx-editor.tsx (~693): failures become unhandled rejections after the history index has already moved; history and rendered theme desync silently.
  • Base instructions still route .pptx reads to parseFile/LLMParsepackages/core/src/runtime/assembly/copilot/instructions.ts (349, 358): "summarize q3.pptx"-style requests bypass deck-review because the skill isn't loaded yet. Fix: exclude .pptx at those two lines.

Cleanup — fine as follow-ups

  • Dead IPC surfaceapps/main/src/ipc.ts (~1337): deck:generateOutline / deck:generateSlide / deck:editSlide have zero callers; ~400 lines in deck_outline.ts plus shared schemas are dead (only reviewDeck is reachable). Suggest deleting.
  • deck-review forces an LLM round-trip on every editdeck.ts (~394): the tool is mandated before every edit, but the payload the agent needs is computed locally before the model call. A contents-only flag (or a deck-read tool) would cut seconds of latency from the most common path.
  • Smaller duplications worth a pass: XML-escape helpers ×4 and EMU/slide-size constants duplicated in packages/shared/src/pptx, twin writeBuffer/writeText bodies in files.ts, triple serialization per theme click, double unzip per deck open, deck tool names hand-listed in four places, deck_outline.ts bypassing generateObjectSafe/taskModels.

Deck tools now read and write transactionally - readBuffer returns an
etag, every tool write is guarded, and a concurrent editor save makes
the tool fail with a conflict instead of clobbering it. The editor's
reload only adopts the new etag after the idle check passes, and a
deleted file becomes a proper conflict instead of endless ENOENT toasts.
Bullet edits on a single-text-shape slide rebuild instead of silently
no-oping. New deck-restructure tool exposes the slide delete and reorder
the prompts already promised. Theme changes and theme undo persist
before touching history or the screen. Base instructions no longer route
.pptx reads to parseFile.

Also fixes the symptom the reviewer hit: scheduleSave never cleared its
timer handle when the debounce fired, so isIdle stayed false for the
session and every assistant write showed the banner instead of reloading.
@prakhar1605

Copy link
Copy Markdown
Collaborator Author

@Gagancreates Thanks for running it locally — the live-reload symptom you hit was the
real bug: scheduleSave never cleared its timer handle when the debounce
fired, so isIdle stayed false for the rest of the session and every
assistant write went to the banner instead of reloading. Fixed and pinned.

All 8 correctness items are fixed with a regression test each, and every
new test was checked to fail against the pre-fix code:

  1. Tools now read and write transactionally (readBuffer returns an etag);
    a concurrent editor save makes the tool fail with a conflict.
  2. The reload only adopts the loaded etag after the idle check passes.
  3. Missing file is a typed conflict now — banner offers Recreate, no
    endless toasts.
  4. Bullets with no body shape force a replace instead of a silent noop.
  5. New deck-restructure tool exposes delete + reorder.
    6 & 7. Theme change and theme undo persist before touching history or
    the screen.
  6. .pptx excluded at both instruction lines.

Cleanup items (dead IPC, deck-review latency, the duplication pass) —
agreed on all three, tracking them as a follow-up PR rather than growing
this one.

@Gagancreates
Gagancreates merged commit a80d61c into rowboatlabs:main Aug 20, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants