corespeed-slide
/install corespeed-slide
Corespeed PPTX — PowerPoint Generation with JSX
Generate professional .pptx files using TypeScript JSX via @pixel/pptx.
Workflow
- Write a
.tsxfile that exports adeckvariable - Run the generator to produce the
.pptx
Usage
deno run --allow-read --allow-write --config {baseDir}/scripts/deno.json {baseDir}/scripts/generate.ts slides.tsx output.pptx [--json]
- First arg: path to your
.tsxslide file (mustexport const deck = ...) - Second arg: output
.pptxfilename --json— structured JSON output for agent consumption
Design Principles
These rules are distilled from the three masters of presentation design:
- Edward Tufte (Yale professor, The Visual Display of Quantitative Information) — maximize data-ink ratio, remove chartjunk, every pixel must earn its place
- Nancy Duarte (designed Al Gore's An Inconvenient Truth, author of Slide:ology) — one idea per slide, visual contrast creates meaning, the audience is the hero
- Garr Reynolds (author of Presentation Zen) — restraint, whitespace, signal over noise, simplicity is the ultimate sophistication
Follow these rules for every deck:
- One idea per slide (Duarte). If you need a second point, make a second slide.
- Font ≥ 24pt body, ≥ 32pt titles. If the audience can't read it from the back row, it's too small.
- Max 3 colors (Reynolds). One primary, one accent, one neutral. Derive the rest as tints/shades.
- Whitespace is design (Reynolds). Generous padding (≥ 0.5in slide margins, ≥ 0.2in element padding). Never fill every inch.
- Contrast over decoration (Duarte). No drop shadows unless essential. No gradients for ornament. Use contrast (size, weight, color) to create hierarchy.
- Data-ink ratio (Tufte). Every pixel should convey information. Remove gridlines, borders, and labels that don't add meaning.
- Visual hierarchy. Title → Key number/chart → Supporting text. The eye should know where to go instantly.
- Consistent rhythm. Same gaps, same padding, same font sizes across all slides. Use
layoutdefaults on\x3CPresentation>. - 16:9 widescreen. Always set
slideWidth={u.in(13.33)} slideHeight={u.in(7.5)}on\x3CPresentation>. The library defaults to 4:3 which looks wrong on modern screens.
Preventing Text Overflow (critical)
The library uses fixed-size containers. Text that exceeds the container will overflow and be clipped. Follow these rules strictly:
- Use
growinstead of fixedhwhenever possible. Inside\x3CRow>or\x3CColumn>, prefergrow={1}over explicith={u.in(X)}. The layout engine will calculate the correct size. - Budget height for text. Each line of 14pt text needs ~0.3in. A card with title + 4 bullet points needs at minimum:
0.3 (title) + 4×0.3 (bullets) + 0.6 (padding) = 1.8in. Always round up. - Fewer words, never more containers. If text doesn't fit, shorten the text — don't shrink the font or the container.
- Max 5 bullet points per card. If you need more, split into two cards or two slides.
- Test the math. For
\x3CAlign>with explicith: count lines × 0.3in + padding × 2. If the result > h, increase h or cut text. - Leave 20% headroom. If you calculate 2.0in needed, set h to at least 2.4in.
- Never nest
\x3CAlign>with tight heights inside\x3CStack>. Use\x3CColumn>withgrowinstead — it flexes to fit content. - Safe card pattern:
{/* ✅ GOOD: grow handles height automatically */} \x3CColumn> \x3CShape preset="roundRect" style={cardStyle} grow={1}> \x3CText.P style={titleStyle}>Title\x3C/Text.P> \x3CText.P style={bodyStyle}>Body text here\x3C/Text.P> \x3C/Shape> \x3C/Column> {/* ❌ BAD: fixed h too small, text will overflow */} \x3CAlign x="center" y="center" w={u.in(3)} h={u.in(1.5)}> \x3CText> \x3CText.P>Title\x3C/Text.P> \x3CText.P>Line 1\x3C/Text.P> \x3CText.P>Line 2\x3C/Text.P> \x3CText.P>Line 3\x3C/Text.P> \x3CText.P>Line 4\x3C/Text.P> {/* overflows! */} \x3C/Text> \x3C/Align>
Table Overflow Prevention (critical)
Tables are the most common source of overflow. Follow these rules:
-
Column widths must sum to ≤ content width. For 16:9 slide with 1in padding each side: content width = 11.33in. All
colsvalues must sum to ≤ 11.33in.// ✅ GOOD: 2.5 + 4.0 + 4.0 = 10.5in \x3C 11.33in \x3CTable cols={[u.in(2.5), u.in(4.0), u.in(4.0)]}> // ❌ BAD: 3.0 + 5.0 + 5.0 = 13.0in > 11.33in → overflows right \x3CTable cols={[u.in(3.0), u.in(5.0), u.in(5.0)]}> -
Give tables
growweight in\x3CColumn>.\x3CColumn>distributes height equally by default. A table with 5 rows needs more space than a title. Usegrowto allocate proportionally:// ✅ GOOD: table gets 4x the space of title \x3CColumn> \x3CText.P style={titleStyle}>Title\x3C/Text.P> {/* grow=1 default */} \x3CTable cols={[...]} grow={4}> {/* gets 4x height */} \x3CTable.Row>...\x3C/Table.Row> ... \x3C/Table> \x3C/Column> // ❌ BAD: Column splits equally, table gets squeezed \x3CColumn> \x3CText.P>Label\x3C/Text.P> \x3CText.P>Title\x3C/Text.P> \x3CTable cols={[...]}> {/* gets only 1/3 of height → rows overflow */} ...5 rows... \x3C/Table> \x3C/Column> -
Budget row height. Each row of 12pt text needs ~0.45in (text + padding). Header row: 0.45in. A 5-row table needs:
0.45 + 4×0.45 = 2.25inminimum. -
Keep cell text short. Max ~40 chars per cell. If text wraps to 2 lines, the row needs 0.65in instead of 0.45in.
-
Prefer fewer rows. Max 5-6 data rows per slide. If you need more, split into two slides.
Color Palettes (reference)
| Style | Background | Primary | Accent | Text |
|---|---|---|---|---|
| Dark (OpenAI) | 0D0D0D |
10A37F |
6E42D3 |
FFFFFF |
| Warm (Anthropic) | FDF6EE |
D97706 |
1F4E79 |
1C1917 |
| Clean (Apple) | FFFFFF |
007AFF |
FF3B30 |
1D1D1F |
| Academic (Stanford) | FFFFFF |
8C1515 |
2E2D29 |
2E2D29 |
| Neutral (Stripe) | F6F9FC |
635BFF |
0A2540 |
0A2540 |
Pick one palette. Don't mix.
Page Numbers
The library has no built-in page numbers. Use \x3CPositioned> on every slide:
// Helper — put at top of every .tsx file
const TOTAL = 10; // update to actual slide count
// ⚠️ CRITICAL: \x3CPositioned> coordinates are RELATIVE TO CONTENT AREA (after slide padding).
// For 16:9 slide (13.33 x 7.5in) with padding 0.8/1.0/0.7/1.0:
// Content area = (13.33 - 1.0 - 1.0) x (7.5 - 0.8 - 0.7) = 11.33 x 6.0in
// Bottom-right page number: x = contentWidth - 1.0, y = contentHeight - 0.4
const PageNum = ({ n }: { n: number }) => (
\x3CPositioned x={u.in(10.33)} y={u.in(5.6)} w={u.in(1)} h={u.in(0.4)}>
\x3CText.P style={{ fontSize: u.font(12), fontColor: textColor, align: "right" }}>
{n} / {TOTAL}
\x3C/Text.P>
\x3C/Positioned>
);
// Use on every slide — LAST child of \x3CSlide> (renders on top, never occluded)
\x3CSlide background={bg}>
\x3CColumn>
{/* ...slide content... */}
\x3C/Column>
\x3CPageNum n={1} /> {/* ← MUST be last child for highest z-order */}
\x3C/Slide>
Rules:
- Always add page numbers. They help the audience track progress and reference slides.
\x3CPositioned>is relative to content area, NOT the slide origin. Calculate:slideWidth - leftPadding - rightPadding = contentWidth. Position within those bounds.- Use the main text color (not muted) — at 12pt it must be readable.
- Place
\x3CPageNum>as the last child of\x3CSlide>— PPTX has no z-index, rendering order = declaration order. Last element renders on top, so page numbers are never occluded by other content.
Writing Slides
Create a .tsx file. It must export a deck variable:
/** @jsxImportSource @pixel/pptx */
import { Align, clr, Presentation, Slide, Text, u } from "@pixel/pptx";
export const deck = (
\x3CPresentation title="My Deck" slideWidth={u.in(13.33)} slideHeight={u.in(7.5)}>
\x3CSlide background={{ kind: "solid", color: clr.hex("F7F4EE") }}>
\x3CAlign x="center" y="center" w={u.in(8)} h={u.in(1.5)}>
\x3CText.P style={{ fontSize: u.font(32), bold: true }}>
Hello, World!
\x3C/Text.P>
\x3C/Align>
\x3C/Slide>
\x3C/Presentation>
);
Components
Layout
| Component | Purpose |
|---|---|
\x3CPresentation> |
Root container. Props: title, layout |
\x3CSlide> |
Single slide. Props: background, layout |
\x3CRow> |
Horizontal flex layout. Has \x3CRow.Start>, \x3CRow.End> |
\x3CColumn> |
Vertical flex layout. Has \x3CColumn.Start>, \x3CColumn.End> |
\x3CStack> |
Overlapping layers |
\x3CAlign x y w h> |
Center/align a single child |
\x3CPositioned x y w h> |
Absolute positioning |
Content
| Component | Purpose |
|---|---|
\x3CText> |
Multi-paragraph text body. Props: gap, style |
\x3CText.P> |
Single paragraph |
\x3CText.Span> |
Inline text run |
\x3CText.Bold>, \x3CText.Italic>, \x3CText.Underline> |
Inline formatting |
\x3CText.Link href="..."> |
Hyperlink |
\x3CShape preset="..."> |
Shape: rect, roundRect, ellipse, etc. |
\x3CImage src={bytes} w={...} h={...} /> |
Embed image (Uint8Array) |
\x3CTable cols=[...]> |
Table with \x3CTable.Row> and \x3CTable.Cell> |
Charts
| Component | Purpose |
|---|---|
\x3CChart.Bar data={[...]} category="key" series={[...]} /> |
Bar chart |
\x3CChart.Line data={[...]} category="key" series={[...]} /> |
Line chart |
\x3CChart.Pie data={[...]} category="key" series={[...]} /> |
Pie chart |
\x3CChart.Donut data={[...]} category="key" series={[...]} /> |
Donut chart |
Units & Colors
import { u, clr } from "@pixel/pptx";
u.in(1) // inches
u.cm(2.5) // centimeters
u.pt(12) // points
u.pct(50) // percentage
u.font(24) // font size (hundredths of a point)
clr.hex("1F4E79") // hex color (no #)
Styling
Style props are plain objects. Use style on any component:
const style = {
fill: { kind: "solid", color: clr.hex("1F4E79") },
fontSize: u.font(24),
fontColor: clr.hex("FFFFFF"),
bold: true,
italic: false,
align: "center",
verticalAlign: "middle",
padding: u.in(0.2),
shadow: {
color: clr.hex("000000"),
blur: u.emu(12000),
distance: u.emu(4000),
angle: 50,
alpha: u.pct(18),
},
bullet: { kind: "char", char: "•" },
};
Backgrounds support solid, linear-gradient, and image.
Example: Professional Multi-Slide Deck (Anthropic-warm style)
/** @jsxImportSource @pixel/pptx */
import {
Align, Chart, clr, Column, Presentation, Row, Shape, Slide,
Stack, Table, Text, u, type Style,
} from "@pixel/pptx";
// --- Design tokens (pick ONE palette, use everywhere) ---
const bg = clr.hex("FDF6EE"); // warm cream
const card = clr.hex("FFFFFF");
const primary = clr.hex("D97706"); // amber
const dark = clr.hex("1C1917"); // near-black
const muted = clr.hex("78716C"); // warm gray
const accent = clr.hex("1F4E79"); // deep blue for charts
// --- Reusable styles ---
const s = {
heroBar: {
fill: { kind: "solid", color: primary },
verticalAlign: "middle",
padding: { top: u.in(0.3), right: u.in(0.5), bottom: u.in(0.3), left: u.in(0.5) },
} satisfies Style,
h1: { fontSize: u.font(36), fontColor: card, bold: true } satisfies Style,
subtitle: { fontSize: u.font(16), fontColor: clr.hex("FEF3C7") } satisfies Style,
h2: { fontSize: u.font(24), fontColor: dark, bold: true } satisfies Style,
body: { fontSize: u.font(14), fontColor: muted } satisfies Style,
bigNum: { fontSize: u.font(48), fontColor: primary, bold: true, align: "center" } satisfies Style,
bigLabel: { fontSize: u.font(14), fontColor: muted, align: "center" } satisfies Style,
cardBox: {
fill: { kind: "solid", color: card },
padding: u.in(0.3),
shadow: { color: clr.hex("000000"), blur: u.emu(8000), distance: u.emu(2000), angle: 90, alpha: u.pct(8) },
} satisfies Style,
};
export const deck = (
\x3CPresentation title="Q2 Review" slideWidth={u.in(13.33)} slideHeight={u.in(7.5)} layout={{
slidePadding: u.in(0.6),
rowGap: u.in(0.35),
columnGap: u.in(0.35),
}}>
{/* Slide 1: Title — one idea only */}
\x3CSlide background={{ kind: "solid", color: bg }}>
\x3CColumn>
\x3CShape preset="roundRect" h={u.in(1.6)} style={s.heroBar}>
\x3CText.P style={s.h1}>Q2 2025 Review\x3C/Text.P>
\x3CText.P style={s.subtitle}>Growth ahead of plan · 15% QoQ\x3C/Text.P>
\x3C/Shape>
\x3CRow>
{/* Big number cards — Tufte: let the data speak */}
{[
{ num: "$1.2M", label: "Revenue" },
{ num: "15%", label: "Growth" },
{ num: "61", label: "NPS" },
].map(({ num, label }) => (
\x3CStack grow={1}>
\x3CShape preset="roundRect" style={s.cardBox} />
\x3CAlign x="center" y="center" w={u.in(2.5)} h={u.in(2.5)}>
\x3CText gap={u.in(0.1)}>
\x3CText.P style={s.bigNum}>{num}\x3C/Text.P>
\x3CText.P style={s.bigLabel}>{label}\x3C/Text.P>
\x3C/Text>
\x3C/Align>
\x3C/Stack>
))}
\x3C/Row>
\x3C/Column>
\x3C/Slide>
{/* Slide 2: Chart — one chart, full focus */}
\x3CSlide background={{ kind: "solid", color: bg }}>
\x3CColumn>
\x3CText.P style={s.h2}>Revenue by Quarter\x3C/Text.P>
\x3CStack grow={1}>
\x3CShape preset="roundRect" style={s.cardBox} />
\x3CAlign x="center" y="center" w={u.in(8)} h={u.in(4)}>
\x3CChart.Bar
data={[
{ q: "Q1", rev: 0.8 }, { q: "Q2", rev: 1.2 },
{ q: "Q3", rev: 1.0 }, { q: "Q4", rev: 1.5 },
]}
category="q"
series={[{ name: "Revenue ($M)", value: "rev", color: accent }]}
labels
valueAxis={{ min: 0, max: 2 }}
/>
\x3C/Align>
\x3C/Stack>
\x3C/Column>
\x3C/Slide>
\x3C/Presentation>
);
Notes
- No manual setup required. Deno auto-downloads
@pixel/pptxfrom JSR on first run. - The
.tsxfile mustexport const deck = ...(the JSX Presentation element). - Use
--jsonfor structured output:{"ok": true, "file": "...", "size": 1234} - Output opens in PowerPoint, Google Slides, LibreOffice Impress, and Keynote.
- Use timestamps in filenames:
yyyy-mm-dd-hh-mm-ss-name.pptx.
Support
Built by Corespeed. If you need help or run into issues:
- 💬 Discord: discord.gg/mAfhakVRnJ
- 🐦 X/Twitter: @CoreSpeed_io
- 🐙 GitHub: github.com/corespeed-io/skills
- 确保已安装 OpenClaw(本地或 Docker 部署)
- 在对话框中输入安装命令:
/install corespeed-slide - 安装完成后,直接呼叫该 Skill 的名称或使用
/corespeed-slide触发 - 根据 Skill 的参数说明提供必要输入,即可获得结构化输出
corespeed-slide 是什么?
Generate professional PowerPoint (.pptx) presentations using JSX/TSX with Deno. Supports slides, text, shapes, tables, charts (bar, line, pie, donut), images... 它是一个面向 Claude Code / OpenClaw 的 AI Agent Skill 插件,目前累计下载 155 次。
如何安装 corespeed-slide?
在 OpenClaw 或 Claude Code 对话框中运行命令「/install corespeed-slide」即可一键安装,无需额外配置。
corespeed-slide 是免费的吗?
是的,corespeed-slide 完全免费,采用 MIT-0 许可证,可自由下载、安装和使用。
corespeed-slide 支持哪些平台?
corespeed-slide 跨平台运行,可在任意部署了 OpenClaw / Claude Code 的环境中使用(cross-platform)。
谁开发了 corespeed-slide?
由 Zypher Agent(@zypher-agent)开发并维护,当前版本 v1.0.0。