PowerPoint
Use this skill whenever the user asks Codex to create, generate, edit, revise, improve, render, verify, or export a PowerPoint or slide deck, unless the user explicitly requests a different slide skill or tool.
This is the internal PPTX workflow for visually ambitious editable decks.
Core Contract
- Use the existing artifact-tool Node environment with
@oai/artifact-toolfor final deck construction, scratch preview rendering, internal editable-text verification, and.pptxexport. - Use the installed
@oai/artifact-toolpackage from the default Codex runtime node_modules path:~/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules. - Final user-facing response must include a short summary of the deck/slides created or edited, plus standalone Markdown link(s) only to final
.pptxartifact(s), with link text formatted as<deck or slide title> - <filename>and an absolute filesystem path as the target. - Use the output-chip-compatible Markdown link shape, for example
[Smoke Test - output.pptx](/absolute/path/to/output.pptx); replace the example title, filename, and placeholder path with the actual values. If there are multiple requested final decks, put each final.pptxMarkdown link on its own line. Do not wrap final artifact links in backticks or code fences, and do not put them in bullets, headings, or prose sentences. - The final response summary must describe only the user-visible result. Do not mention implementation details such as
artifact_tool, artifact-tool,@oai/artifact-tool, the Node/JS builder, scripts, package manifests, export workflow, verification workflow, or internal tooling unless explicitly requested. - Do not link to or mention support files such as previews, narrative plans, verification records, scripts, package manifests, or scratch files unless explicitly requested. Keep support files on disk if useful.
- If the artifact-tool Node environment is missing, install or refresh the Codex runtime bundle before building.
- Do not use the Python deck-authoring client for final deck construction. Do not start, repair, or depend on the artifact-tool Python RPC daemon.
- If you need to read PDFs locally, use the Codex primary runtime Python with
pypdfsince we don't have the javascript version installed. - Use the platform-native imagegen tool for generated art plates.
- Helper scripts in this skill are Node.js scripts. Do not use Python helper scripts for the JS skill path.
- Use generated reference images directly and let the deck builder place them with
fit: "cover". - Keep the pro workflow quality bar: text-free art-direction plates, deterministic editable design-system geometry, scratch rendered previews, editability checks, and native chart verification.
- Do not use
PptxGenJS,pptxgenjs_helpers,python-pptx, LibreOffice rendering, or screenshot-only slides. - For data charts, graphs, plots, trend lines, bars/columns, scatterplots, pie/donut charts, treemaps, and maps, use native artifact-tool chart objects via
slide.charts.add(...). Do not hand-draw these with shapes, connectors, dots, or text boxes when the chart API can represent them. - Use shapes for cards, labels, icons, non-data diagrams, connectors, annotations, and decorative structure. Shape-drawn sparklines or microcharts are exceptions only when native chart APIs cannot represent the visual; record the exception in scratch verification notes instead of mentioning it in the final response.
- Do not make ImageMagick,
montage, ormagickpart of the required authoring path. Contact sheets are optional and must never block.pptxexport, rendered previews, or lightweight verification. - During eval harness runs, do not spawn sub-agents or delegate. Finish the deck, verification, and final response in the current agent so the run does not fail on model-selection or handoff issues.
Node Authoring Surface
Start deck authoring in a local builder file with:
const {
Presentation,
PresentationFile,
} = await import("@oai/artifact-tool");
For existing decks, also import FileBlob:
const { FileBlob, PresentationFile } = await import("@oai/artifact-tool");
const pptx = await FileBlob.load("input.pptx");
const presentation = await PresentationFile.importPptx(pptx);
Default new deck size is 16:9 at 1280x720:
const presentation = Presentation.create({
slideSize: { width: 1280, height: 720 },
});
Write final deck deliverables under outputs/<unique_thread_id>/ unless the user gives an explicit output folder. The final deck artifact is output.pptx; any agent-authored narrative_plan.md created during planning is a support file. Keep previews, inspect records, reference images, and verification scratch under tmp/slides/<deck-id>/.
Using artifact_tool Presentation APIs
Use this quick surface first when editing the JS builder. It is a curated subset of the full TypeScript presentation API, not the full reference.
Build rules
- Use
@oai/artifact-toolfrom Node for final deck construction, rendering, editable-text verification, and.pptxexport. - All size and positioning units are pixels unless a specific API notes otherwise.
- Author meaningful content as editable PowerPoint objects:
slide.shapes,shape.text,slide.tables,slide.charts, andslide.speakerNotes. - Always use
slide.charts.add(...)for data-backed charts and graphs that the API can express; do not construct bar, line, scatter, pie, treemap, map, axis, series, or data-label systems out of raw shapes/text boxes. - Treat native charts as designed slide elements, not default exports: set chart fonts, label sizes, gridlines, axis lines, legend placement, series colors/strokes, and plot area styling so the chart matches the rest of the slide.
- Use imagegen output as text-free visual plates only; keep real slide words, labels, numbers, tables, and chart text in editable objects.
- Common geometry strings include
"rect","roundRect","ellipse","rightArrow", and"connector"; usegeometry: "custom"withcustomPathswhen a preset cannot express the vector shape. - Render with
presentation.export({ slide, format: "png", scale: 1 })before final export, and use scratch inspect records to confirm important copy is editable.
Conventions and gotchas
- New deck:
Presentation.create({ slideSize: { width: 1280, height: 720 } }). Presentationis the in-memory deck object;PresentationFileis for.pptximport/export.- Existing deck:
PresentationFile.importPptx(await FileBlob.load("input.pptx")). - Export:
const pptx = await PresentationFile.exportPptx(presentation); await pptx.save("output.pptx"). - Rendering:
await presentation.export({ slide, format: "png", scale: 1 }). - Fill values can be theme aliases or hex strings:
"accent1","background1","text1","#FF6600","#11223380". - Stroke values use
{ style: "solid" | "dashed" | "dotted" | "dash-dot" | "dash-dot-dot", fill, width }. - Gradient stop offsets are
0..100000; rotation is in degrees. - Set font faces with
typeface. - Style assignment is paragraph-scoped:
shape.text.get("word").style = "heading1"styles the whole paragraph and clears inline overrides. Apply inline overrides after assigning styles. shape.text.insetsandfontSizeare pixels, butspacingBeforeandspacingAfterare 1/100 point.- Use
fit: "cover"to fill a frame with possible cropping; usefit: "contain"to preserve the entire image. - For local images, read bytes with
readImageBlob(...)and pass an exactArrayBufferas{ blob: await readImageBlob(path) }. - For line charts, set
chart.lineOptions.grouping = "standard"before export. Leaving grouping unset can produce repair-prone PowerPoint chart XML. - Avoid
chart.chartFillin PPTX exports for now; prefer a panel/background shape behind the chart pluschart.plotAreaFill. If whole chart-space styling is required, verify the file opens in PowerPoint without repair. - Match chart typography to the slide: set
chart.titleTextStyle.typeface,chart.legend.textStyle.typeface,chart.xAxis.textStyle.typeface,chart.yAxis.textStyle.typeface, andchart.dataLabels.textStyle.typefaceto the same body/title font family used elsewhere.
Local image helper:
const fs = await import("node:fs/promises");
async function readImageBlob(imagePath) {
const bytes = await fs.readFile(imagePath);
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
}
Core deck and slide APIs
const { FileBlob, Presentation, PresentationFile } = await import("@oai/artifact-tool")const presentation = Presentation.create({ slideSize: { width: 1280, height: 720 } })const imported = await PresentationFile.importPptx(await FileBlob.load("input.pptx"))const pptx = await PresentationFile.exportPptx(presentation); await pptx.save("output.pptx")const slide = presentation.slides.add()const { slide, index } = presentation.slides.insert({ after: presentation.slides.getItem(0) })presentation.slides.getItem(0),presentation.slides.items,presentation.slides.countslide.duplicate(),slide.moveTo(index),slide.delete()slide.background.fill = "background1"or a solid/gradient fill object
Shapes and rich text
slide.shapes.add({ geometry, position, fill, line })- Common
geometryvalues are non-exhaustive; use known preset names first and verify each shape by rendering the deck. shape.position = { left?: number, top?: number, width?: number, height?: number, rotation?: number, horizontalFlip?: boolean, verticalFlip?: boolean }shape.fill = "accent1";shape.line.width = 1.5;shape.rotation = 15shape.text = "Quarterly Results"orshape.text = ["Line 1", "Line 2"]- Round-rect corner control:
adjustmentList: [{ name: "adj", formula: "val 16667" }];50000is pill-like max rounding. shape.text.fontSize = 28,shape.text.bold = true,shape.text.color = "text1"shape.text.typeface = "Aptos",shape.text.alignment = "center",shape.text.verticalAlignment = "middle"shape.text.insets = { left: 12, right: 12, top: 8, bottom: 8 }shape.text.autoFit = "shrinkText"or"resizeShapeToFitText"only when needed.shape.text.get("literal text").bold = true,shape.text.get("literal text").style = "heading1"shape.text.add("New paragraph"),shape.text.replace("Old", "New")
Detached rich text:
const { Text } = await import("@oai/artifact-tool");
const block = Text.create(["Quarterly Business Review", "Q2 Execution Plan"]);
block.get("Quarterly Business Review").style = "title";
block.get("Quarterly Business Review").fontSize = 38;
block.get("Q2 Execution Plan").color = "accent1";
shape.text = block;
Connector example:
slide.shapes.add({
geometry: "connector",
kind: "elbow",
from: sourceShape,
fromIdx: 3,
to: targetShape,
toIdx: 1,
line: { style: "solid", fill: "accent1", width: 2 },
head: { type: "arrow", width: "med", length: "med" },
});
Images, tables, charts, and notes
slide.images.add({ blob, fit: "cover", alt }),slide.images.add({ dataUrl, fit: "contain", alt }),slide.images.add({ uri, alt })image.position = { left, top, width, height }image.replace({ blob: replacementBlob, alt: "Updated hero" })image.crop = { left: 0.05, top: 0.05, right: 0.05, bottom: 0.05 }image.geometry = "roundRect"or passgeometry: "roundRect"at creation for masks- Text-free plate pattern:
const plate = slide.images.add({ blob: await readImageBlob("tmp/slides/pro-reference-images/slide-01.png"), fit: "cover", alt: "Text-free visual plate", }); plate.position = { left: 0, top: 0, width: 1280, height: 720 }; const table = slide.tables.add([["Metric", "North", "EMEA"], ["Bookings", 120, 94]])const table = slide.tables.add({ rows, columns, left, top, width, height, values })table.getCell(row, col).value = "APAC",table.setValues(matrix)table.merge({ startRow, endRow, startColumn, endColumn })table.style = "TableStyleMedium9";table.columns.get(0).width = 220table.cells.block({ row, column, rowCount, columnCount }).fill = "#0F172A"- Use
slide.charts.add(...)for every data-backed chart or graph; do not implement bar/line/scatter/pie/treemap/map charts withslide.shapes.add(...). const chart = slide.charts.add("line" | "bar" | "scatter" | "pie" | "treemap" | "map" | "bar3D")chart.position = { left, top, width, height }chart.title = "Quarterly Revenue",chart.categories = ["Q1", "Q2", "Q3", "Q4"]const series = chart.series.add("Revenue"); series.values = [120, 140, 180, 210]; series.categories = chart.categoriesseries.fill = "accent1";series.stroke = { width: 2, style: "solid", fill: "accent1" }chart.hasLegend = true;chart.legend.position = "bottom"chart.barOptions.direction = "column";chart.barOptions.grouping = "stacked"chart.dataLabels.showValue = true;chart.dataLabels.position = "outEnd"- For line charts:
chart.lineOptions.grouping = "standard";chart.lineOptions.smooth = false - Match chart fonts to slide fonts:
chart.titleTextStyle.typeface = FONT.title;chart.legend.textStyle.typeface = FONT.body;chart.xAxis.textStyle.typeface = FONT.body;chart.yAxis.textStyle.typeface = FONT.body;chart.dataLabels.textStyle.typeface = FONT.body - Tune chart aesthetics with chart APIs:
chart.titleTextStyle.fill,chart.titleTextStyle.fontSize,chart.legend.textStyle.fontSize,chart.xAxis.textStyle.fontSize,chart.yAxis.textStyle.fontSize,chart.yAxis.majorGridlines,chart.xAxis.line,chart.yAxis.line,chart.plotAreaFill,series.fill, andseries.stroke - Prefer a separate
slide.shapes.add({ geometry: "roundRect", ... })panel behind the chart instead ofchart.chartFilluntil chart-space fill export is verified. slide.speakerNotes.setText("Presenter notes"),slide.speakerNotes.append(["Next point", "Final point"])
Theme, styles, auto-layout, and placeholders
presentation.theme.colorScheme = { name, themeColors: { accent1, accent2, bg1, bg2, tx1, tx2, ... } }presentation.theme.hexColorMapconst style = presentation.styles.add("metricLabel"); setstyle.fontSize,style.bold,style.color,style.typefacepresentation.styles.describe()orpresentation.styles.describe("title")- Import
AutoLayout,AutoLayoutAlign, andAutoLayoutDirectiononly when using auto-layout. slide.autoLayout(shapes, { direction: AutoLayoutDirection.horizontal, frame: "slide", align: AutoLayoutAlign.topCenter, horizontalGap: 32, verticalPadding: 48 })AutoLayout.apply(slide, shapes, options)is the static entrypoint.- Use direct shapes/text for non-chart one-off generated layouts. If layout reuse is useful:
const layout = presentation.layouts.add("Title Slide");layout.placeholders.add({ name: "Title", type: "title", index: 1, text: "Title" });slide.setLayout(layout);slide.placeholders.getItem("title").text = "Kickoff".
Required Workflow
- Plan the deck:
- Define audience, narrative arc, slide list, source plan, visual system, icon/style plan, and likely hero/diagram/crop assets.
- Create or update an agent-authored
narrative_plan.mdin the final output directory during this step unless the user explicitly asks for PPTX-only output. Include audience, objective, narrative arc, slide list, source plan, visual system, imagegen plan, asset needs, and editability plan. Do not rely on the JS builder to create or overwrite this file. - If the source plan includes a local PDF, first extract readable text locally with runtime
pypdf. Use web/source search only if local extraction fails, source data is incomplete, or the task requires current external context. - Separate every slide into editable text and generated visual. Editable text includes titles, subtitles, body, callouts, dates, timeline labels, captions, tables, chart labels, code/UI text, and important diagram labels.
- Use modern sans by default:
Poppinsfor titles andLatofor body/captions. Use formal serif only when appropriate:Caladeaheadings andLiberation Serifbody/captions.
- Prepare native-imagegen prompts:
- Write one outline file with an intro paragraph and exactly one section per slide.
- Use this skill's
scripts/prepare_reference_prompts.jsto create one prompt per slide and a manifest with expected filenames. - Ask for text-free art-direction plates: broad calm zones, atmosphere, texture, collage, hero imagery, motifs, unlabeled diagrams/charts, abstract UI chrome, placeholder strokes, and icon containers.
- Do not ask imagegen for real slide words, labels, dates, chart text, table text, citations, logos, code, or readable annotations.
- Store selected generated reference images in
tmp/slides/pro-reference-images/with exact namesslide-01.png,slide-02.png, and so on. - If native imagegen saves under
$CODEX_HOME/generated_images/..., copy or move only the selected final images into the reference directory. Do not leave project-bound reference assets only in the default generated-images location. - Generate slide 1 first as the visual-system setter. For slides 2..N, use slide 1 as a visual style reference when the platform supports it; otherwise carry slide 1's palette, motif, density, and image treatment in the prompt.
- If a generated image is not exactly 16:9, do not preprocess it. The JS builder uses
fit: "cover"to place it on the slide canvas.
Example:
SKILL_DIR=<path-to-installed-slides-skill>
node "$SKILL_DIR/scripts/prepare_reference_prompts.js" \
tmp/slides/pro-outline.md \
tmp/slides/pro-reference-images \
--slide-count 6 \
--deck-size 1280x720 \
--style-guidance "text-free art-direction plates, broad calm regions for editable PowerPoint cards and title blocks, warm editorial palette, rounded hero image frames, Lucide-style icon containers, unlabeled diagrams, placeholder strokes only"
- Generate the art plates:
- Use the platform-native imagegen tool once per slide prompt.
- Keep the final selected image for each slide as
slide-XX.pngin the reference directory from the manifest. - Do not generate or keep image-rendered words, numbers, table text, labels, logos, source text, or code as part of the plate.
- If a visual crop or isolated hero image is needed, prefer regenerating a cleaner text-free plate or placing the full plate with PowerPoint geometry. Avoid raster-cropping helpers in this JS skill path.
- Build the editable deck in JS:
- Start from
scripts/init_pro_deck_builder_js.jsor write a bespoke Node builder with the same architecture: theme constants, slide data, source notes,addText,addCard,addTitleBlock,addHeader,addPlate, native chart helpers wrappingslide.charts.add(...), table/card helpers, icon helpers, render/export helpers, and inspect-record helpers. - Treat generated art plates as visual direction and crop sources. The authored JS layer owns text, cards, labels, native chart objects, icons, and important shapes.
- A full-slide generated art plate may sit behind the deterministic editable layer only when it is text-free.
- For local image files, read bytes with
fs.readFile, pass an exactArrayBuffervia{ blob }, and retain the source path only in scratch inspect records; do not rely on{ path }for finalexportPptxmedia embedding. - When using
scripts/init_pro_deck_builder_js.js, keep the generated siblingbuild/node_modules/@oai/artifact-toolpackage link when the script creates one. Node resolves@oai/artifact-toolfrom the builder file's directory, so this makes shell-run eval builders import the default Codex runtime package while preservingawait import("@oai/artifact-tool")in the builder. - Do not put editable text into generated boxes by guesswork. Author matching editable boxes yourself, or place a clean authored card/panel over a broad calm art region.
Example scaffold:
SKILL_DIR=<path-to-installed-slides-skill>
node "$SKILL_DIR/scripts/init_pro_deck_builder_js.js" \
--deck-id demo-slides \
--output tmp/slides/build_demo_pro_js.js \
--slide-count 3 \
--reference-dir tmp/slides/pro-reference-images \
--out-dir outputs/demo-slides
Then edit SLIDES/SOURCES and slide-specific layout functions, render previews, verify, and export by running the generated builder in the existing Node environment.
Run local builder files from a workspace where @oai/artifact-tool is resolvable. Generate builders with scripts/init_pro_deck_builder_js.js so the builder directory has a local build/node_modules/@oai/artifact-tool package link to the default Codex runtime package and a package.json with type: "module". Prefer the default Codex runtime Node binary:
~/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node <builder-file>
Do not use a different node binary for @oai/artifact-tool builders unless you first prove the package resolves. Do not run pnpm exec from the repo root, and do not run a builder without a sibling or otherwise resolvable node_modules/@oai/artifact-tool; Node resolves the package from the builder path.
- Verify:
- Render final slides from JS with
presentation.export({ slide, format: "png", scale: 1 }); save previews under scratchtmp/slides/<deck-id>/preview/, not in the final output folder. - Write scratch inspect records from JS helper wrappers. Each editable textbox record must include
kind,slide,role,text,textChars,textLines, andbbox. - Use the scratch inspect records to confirm major planned copy exists as editable text/table/chart/notes data, not only in images.
- Verify data charts are native chart objects: the builder should contain
slide.charts.add(...)for every chart-like visual, and the exported PPTX should contain native chart XML parts such asppt/slides/charts/chart*.xmlwhen the deck includes charts. - When inspecting rendered previews, treat charts as first-class design objects: chart typography should match the surrounding slide font system, colors should use the deck palette, axes/gridlines should be intentional and not default-heavy, labels and legends should be readable without crowding, and the chart should look visually integrated with the panel/card it sits in.
- For every native chart, use chart APIs to improve aesthetics before accepting a render: set
titleTextStyle,legend.textStyle,xAxis.textStyle,yAxis.textStyle,dataLabels.textStyle, axis/gridline strokes, series fill/stroke, legend position, andplotAreaFillas appropriate. Do not accept mismatched default chart fonts or generic Office chart styling when the rest of the slide has a designed visual system. - Track every render/verify attempt in scratch
tmp/slides/<deck-id>/verification/render_verify_loops.ndjson; the template appends one record each time previews andoutput.pptxare exported. Stop after 3 total render/verify/fix loops, including the initial render. - Run
ls -1 tmp/slides/<deck-id>/preview/slide-*.pngfrom the workspace and inspect the exact absolute preview paths at readable size before accepting the deck. - Check every slide for overlapping elements, text overflow or clipping, decorative rules built for single-line text after a title wraps, footer/source collisions, gaps under 0.3", edge margins under 0.5" outside intentional headers/footers, uneven spacing, inconsistent alignment, low-contrast text/icons, excessive wrapping from narrow text boxes, leftover placeholder content, and table/card/layout-box font sizes that feel unnaturally small or visually timid for their containers.
- Font size should feel natural and aesthetically balanced for the layout: not so large that it overflows, wraps excessively, or collides with boundaries, but not so small that tables, labels, callouts, or card text look like footnotes unless they are genuinely footnotes/citations. Prefer increasing container size, reducing copy, simplifying a table, or splitting dense content before shrinking important text.
- Fix every actionable visual issue in the JS builder, rerender, and repeat until the slide is clean or the 3-loop cap is reached. Loop 1 is the initial render; loops 2 and 3 are the only allowed fix/rerender passes.
- Do not run
scripts/pro_deck_quality_check.jsfor normal deck creation. It is reserved for explicit debug, eval, or manual investigation requests.
Completion Criteria
Complete only when:
- The final deck is exported as
output.pptx; any agent-authorednarrative_plan.mdremains a support file that is not surfaced unless requested. - Internal implementation check only: the final deck is built, rendered, verified, and exported with the required runtime. Do not mention this tooling or workflow in the final response.
- Important copy was internally verified as editable.
- Data-backed charts and graphs use native
slide.charts.add(...); any shape-drawn sparkline/microchart exception is recorded in scratch verification notes. - Rendered previews were inspected, and any actionable visual issues were fixed within the 3-loop cap.
- The final response includes a short summary of the deck/slides created or edited and standalone Markdown link(s) only to final
.pptxartifact(s), using<deck or slide title> - <filename>as the link label and an absolute filesystem path as the target. - The final response does not mention implementation details such as
artifact_tool, artifact-tool,@oai/artifact-tool, the Node/JS builder, scripts, package manifests, export workflow, verification workflow, or internal tooling unless explicitly requested. - The final response does not mention or link to
narrative_plan.md, rendered previews, verification records, scripts, package manifests, scratch files, or other support artifacts unless explicitly requested.
References
Use the installed @oai/artifact-tool TypeScript presentation docs when they are available in the current environment. Do not assume local checkout-specific absolute paths exist in packaged installs.