Getting it into your agent
One page per mod, every tool's command on it. A separate URL per tool would split the same page into five that compete with each other.
npx agentmods add skills/guyoung/boxagnts/pptx-generatornpx skills add guyoung/boxagnts --skill pptx-generatorgit clone --depth 1 https://github.com/guyoung/boxagntsWrote this? Show the measurements
A badge with what this costs and how it scanned, read live from this page, so it follows the numbers instead of freezing them. Markdown for a README, HTML for a documentation site or a project page.
[](https://agentmods.dev/skills/guyoung/boxagnts/pptx-generator)<a href="https://agentmods.dev/skills/guyoung/boxagnts/pptx-generator"><img src="https://agentmods.dev/badge/skills/guyoung/boxagnts/pptx-generator.svg" alt="Measured on agentmods" height="20"></a>What it costs to keep this loaded
Counted locally with the o200k_base tokenizer, which is exact for GPT models; Claude uses its own tokenizer and its counts differ. Treat this as one consistent yardstick across the catalogue rather than a bill. Prices are per million input tokens.
| Model | Per session | Once invoked |
|---|---|---|
| Fable 5 | $0.00045 | $0.21925 |
| Opus 5 | $0.00023 | $0.10963 |
| Sonnet 5 | $0.00009 | $0.04385 |
| Haiku 4.5 | $0.00005 | $0.02193 |
Grade A, and why
pptx-generator scanned grade A with 0 findings against 26 rules in 11 categories — prompt injection, anti-refusal, data exfiltration, privilege escalation, supply chain, agent snooping, system-prompt leakage, SSRF and excessive agency — measured 5d ago.
A static scan of the body, not an audit. Every finding is printed with the line that produced it so you can judge whether it matters here. A mod is markdown that instructs an agent; that is exactly why what it instructs is worth reading.
Nothing flagged
None of the 26 patterns this scan looks for appear in this file: no shell pipes, no recursive deletes, no credential paths, no hidden text, no instruction-override or anti-refusal phrasing, no agent-config snooping. That is not a guarantee, it is the absence of the things that are checkable.
How it starts
The opening of the file, as written. The whole thing — 1,510 lines — stays where its author put it; the contents beside it link to each section on GitHub.
CODE SKILL — VERBATIM REQUIRED This skill targets a formal specification (OOXML). Adaptation is prohibited per C2-A Rule 3.
CRITICAL: Execution Constraints
The code below is the SOLE IMPLEMENTATION — it MUST NOT be replaced, simplified, or reimplemented.
Prohibited deviations:
- Do NOT write your own
crc32(),ZipBuilder,parseMarkdownAST(),buildSlideIR(),layoutSlide(),renderSlideXML() - Do NOT skip any OOXML file from the 13+ core file baseline
- Do NOT use compression (method 8 / DEFLATE) — STORE mode only (method 0)
- Do NOT skip
scanAndAssignLinks()— hyperlinks MUST be registered as relationships - Do NOT use remote URLs for images — images MUST be local file paths
Correct Invocation Pattern
- Copy the complete script from
## Runnable Entry Pointbelow - Replace ONLY
{{markdown}},{{output_path}},{{show_slide_numbers}}with actual values - Execute as a single
jsexeccall with NO modifications
Argument Details
| Arg | Type | Required | Default | Description |
|---|---|---|---|---|
markdown |
string |
yes | — | Markdown input for slide generation |
output_path |
string |
yes | — | Output .pptx file path |
show_slide_numbers |
boolean |
no | false |
Show N / M page numbers |
Runnable Entry Point (COMPLETE IIFE, DO NOT MODIFY)
// ===== Runnable Entry Point (IIFE) - DO NOT MODIFY =====
(async function() {
const fs = require("fs");
const pathModule = require("path");
// -- XML Escape --
function esc(s) {
return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
}
// -- ID Generators --
let shapeId = 1;
let relId = 2; // reserve rId1 for slideLayout in slideRels
let imageIndex = 1;
function nextShapeId() { return shapeId++; }
function nextRelId() { return 'rId' + relId++; }
function nextImageName(ext = 'png') { return `image${imageIndex++}.${ext}`; }
// -- CRC32 (ZIP REQUIRED) --
const CRCT = new Uint32Array(256);
for (let i = 0; i < 256; i++) {
let c = i;
for (let j = 0; j < 8; j++) {
c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
}
CRCT[i] = c;
}
function crc32(buf) {
let c = 0xFFFFFFFF;
for (let i = 0; i < buf.length; i++) {
c = CRCT[(c ^ buf[i]) & 0xFF] ^ (c >>> 8);
}
return (c ^ 0xFFFFFFFF) >>> 0;
}
// -- ZIP Builder (STORE mode -- no compression, delayed-concat optimization) --
class ZipBuilder {
constructor() {
this.localChunks = [];
this.centralChunks = [];
this.offset = 0;
this.count = 0;
}
add(path, content) {
const buf = typeof content === 'string' ? Buffer.from(content, 'utf-8') : content;
const crc = crc32(buf);
const nameBuf = Buffer.from(path, 'utf-8');
const localHeader = Buffer.alloc(30);
localHeader.writeUInt32LE(0x04034b50, 0);
localHeader.writeUInt16LE(20, 4);
localHeader.writeUInt16LE(0, 6);
localHeader.writeUInt16LE(0, 8);
localHeader.writeUInt16LE(0, 10);
localHeader.writeUInt16LE(0, 12);
localHeader.writeUInt32LE(crc, 14);
localHeader.writeUInt32LE(buf.length, 18);
localHeader.writeUInt32LE(buf.length, 22);
localHeader.writeUInt16LE(nameBuf.length, 26);
localHeader.writeUInt16LE(0, 28);
this.localChunks.push(localHeader, nameBuf, buf);
const cd = Buffer.alloc(46);
cd.writeUInt32LE(0x02014b50, 0);
cd.writeUInt16LE(20, 4);
cd.writeUInt16LE(20, 6);
cd.writeUInt16LE(0, 8);
cd.writeUInt16LE(0, 10);
cd.writeUInt16LE(0, 12);
cd.writeUInt16LE(0, 14);
cd.writeUInt32LE(crc, 16);
cd.writeUInt32LE(buf.length, 20);
cd.writeUInt32LE(buf.length, 24);
cd.writeUInt16LE(nameBuf.length, 28);
cd.writeUInt16LE(0, 30);
cd.writeUInt16LE(0, 32);
cd.writeUInt16LE(0, 34);
cd.writeUInt16LE(0, 36);
cd.writeUInt32LE(0, 38);
cd.writeUInt32LE(this.offset, 42);
this.centralChunks.push(cd, nameBuf);
this.offset += localHeader.length + nameBuf.length + buf.length;
this.count++;
}
build() {
const localBlock = Buffer.concat(this.localChunks);
const centralBlock = Buffer.concat(this.centralChunks);
const cdSize = centralBlock.length;
const cdOffset = this.offset;
const n = this.count;
const eocd = Buffer.alloc(22);
eocd.writeUInt32LE(0x06054b50, 0);
eocd.writeUInt16LE(0, 4);
eocd.writeUInt16LE(0, 6);
eocd.writeUInt16LE(n, 8);
eocd.writeUInt16LE(n, 10);
eocd.writeUInt32LE(cdSize, 12);
eocd.writeUInt32LE(cdOffset, 16);
eocd.writeUInt16LE(0, 20);
return Buffer.concat([localBlock, centralBlock, eocd]);
}
}
// -- Inline Formatting Parser (Recursive Tokenizer) --
// Handles nested/overlapping formats: **bold `code`**, **[link](url)**, etc.
// Priorities: `code` (highest, no interior formatting) > **bold** > *italic* > [link](url) > plain text
function parseInlineRuns(raw) {
const runs = [];
let i = 0;
while (i < raw.length) {
// Backtick code span — no interior formatting
if (raw[i] === '`') {
const end = raw.indexOf('`', i + 1);
if (end !== -1) {
runs.push({ text: raw.slice(i + 1, end), mono: true });
i = end + 1;
continue;
}
}
// Bold — recursive interior parsing
if (raw[i] === '*' && raw[i + 1] === '*') {
let end = raw.indexOf('**', i + 2);
// If no closing **, try matching at line end (treat as plain text)
if (end === -1) {
runs.push({ text: raw.slice(i, i + 2) });
i += 2;
continue;
}
const inner = raw.slice(i + 2, end);
const innerRuns = parseInlineRuns(inner);
for (const r of innerRuns) {
r.bold = true;
runs.push(r);
}
i = end + 2;
continue;
}
// Italic (single *, not **)
if (raw[i] === '*' && raw[i + 1] !== '*') {
const end = raw.indexOf('*', i + 1);
if (end !== -1) {
const inner = raw.slice(i + 1, end);
const innerRuns = parseInlineRuns(inner);
for (const r of innerRuns) {
r.italic = true;
runs.push(r);
}
i = end + 1;
continue;
}
}
// Link [text](url) — recursive interior parsing for link text
if (raw[i] === '[') {
const closeBracket = raw.indexOf('](', i + 1);
if (closeBracket !== -1) {
const closeParen = raw.indexOf(')', closeBracket + 2);
if (closeParen !== -1) {
const linkText = raw.slice(i + 1, closeBracket);
const linkUrl = raw.slice(closeBracket + 2, closeParen);
const innerRuns = parseInlineRuns(linkText);
for (const r of innerRuns) {
r.link = linkUrl;
runs.push(r);
}
i = closeParen + 1;
continue;
}
}
}
// Plain text — consume until next special character
let end = i + 1;
while (end < raw.length && '`*['.indexOf(raw[end]) === -1) { end++; }
runs.push({ text: raw.slice(i, end) });
i = end;
}
if (runs.length === 0) { runs.push({ text: raw }); }
return runs;
}
// -- Markdown to AST Parser --
function parseMarkdownAST(md) {
const lines = md.split('\n');
const doc = { blocks: [] };
let i = 0;
while (i < lines.length) {
let line = lines[i].trim();
if (line === '---') { doc.blocks.push({ type: 'slide_break' }); i++; continue; }
if (line.startsWith('#')) {
const level = line.match(/^#+/)[0].length;
doc.blocks.push({ type: 'heading', level, text: line.replace(/^#+\s*/, '') });
i++; continue;
}
if (line.startsWith('```')) {
const lang = line.replace('```', '').trim();
i++;
const code = [];
while (i < lines.length && !lines[i].startsWith('```')) { code.push(lines[i]); i++; }
doc.blocks.push({ type: 'code', lang, code: code.join('\n') });
i++; continue;
}
if (line.startsWith('- ')) {
const items = [];
while (i < lines.length && lines[i].startsWith('- ')) { items.push(lines[i].slice(2)); i++; }
doc.blocks.push({ type: 'list', items }); continue;
}
if (/^\d+\.\s/.test(line)) {
const items = [];
while (i < lines.length && /^\d+\.\s/.test(lines[i])) { items.push(lines[i].replace(/^\d+\.\s+/, '')); i++; }
doc.blocks.push({ type: 'ordered_list', items }); continue;
}
if (line.includes('|') && lines[i + 1]?.includes('|')) {
const rows = [];
while (i < lines.length && lines[i].includes('|')) {
const cells = lines[i].split('|').map(c => c.trim()).filter(Boolean);
// Separator row check: each cell matches :?---+:? and we only have the header row so far
const isSeparator = cells.length > 0 && cells.every(c => /^:?-+:?$/.test(c)) && rows.length === 1;
if (!isSeparator) { rows.push(cells); }
i++;
}
doc.blocks.push({ type: 'table', rows }); continue;
}
if (line.startsWith('> ')) {
const items = [];
while (i < lines.length && lines[i].startsWith('> ')) { items.push(lines[i].slice(2)); i++; }
doc.blocks.push({ type: 'blockquote', items }); continue;
}
if (line.startsWith(' or {w=N,h=N} or {50%}
const imgMatch = line.match(/!\[([^\]]*)\]\((.+?)\)(?:\{([^}]+)\})?/);
if (imgMatch) {
const block = { type: 'image', data: imgMatch[2] };
const dimStr = imgMatch[3];
if (dimStr) {
if (dimStr.includes('%')) {
block.widthPct = parseFloat(dimStr);
} else {
const wMatch = dimStr.match(/w=(\d+)/);
const hMatch = dimStr.match(/h=(\d+)/);
if (wMatch) block.width = parseInt(wMatch[1]);
if (hMatch) block.height = parseInt(hMatch[1]);
}
}
doc.blocks.push(block);
}
i++; continue;
}
if (line) {
const runs = parseInlineRuns(line);
doc.blocks.push({ type: 'paragraph', text: line, runs });
}
i++;
}
return doc;
}
// -- Slide IR Builder --
const SLIDE_W = 9144000;
const SLIDE_H = 6858000;
const CHARS_PER_LINE = 80;
const LINE_HEIGHT = 300000;
// CJK-aware character-width estimation.
// CJK/wide characters take ~2× the width of Latin chars in proportional fonts.
// Used instead of raw .length for accurate line-count estimation.
function cjkWeightedLen(text) {
let w = 0;
for (const ch of text) {
const code = ch.charCodeAt(0);
if ((code >= 0x4E00 && code <= 0x9FFF) // CJK Unified Ideographs
|| (code >= 0x3400 && code <= 0x4DBF) // CJK Extension A
|| (code >= 0xF900 && code <= 0xFAFF) // CJK Compatibility
|| (code >= 0x3040 && code <= 0x30FF) // Hiragana + Katakana
|| (code >= 0xAC00 && code <= 0xD7AF) // Hangul Syllables
|| (code >= 0xFF01 && code <= 0xFF60) // Fullwidth Forms
|| (code >= 0xFFE0 && code <= 0xFFE6)) { // Fullwidth Signs
w += 2;
} else {
w += 1;
}
}
return w;
}
function estimateBlockHeight(b) {
if (b.type === 'list' || b.type === 'ordered_list') {
let h = 0;
for (const item of (b.items || [])) { const lines = Math.ceil(cjkWeightedLen(item) / CHARS_PER_LINE) || 1; h += lines * LINE_HEIGHT; }
return h;
}
if (b.type === 'blockquote') {
let h = 0;
for (const item of (b.items || [])) { const lines = Math.ceil(cjkWeightedLen(item) / CHARS_PER_LINE) || 1; h += lines * LINE_HEIGHT; }
return h;
}
if (b.type === 'paragraph') { const text = b.text || ''; const lines = Math.ceil(cjkWeightedLen(text) / CHARS_PER_LINE) || 1; return lines * LINE_HEIGHT; }
if (b.type === 'code') { const codeLines = (b.code || '').split('\n').length; return Math.max(codeLines * 250000, 1500000); }
if (b.type === 'table') { const rows = (b.rows || []).length || 1; return Math.max(rows * 400000, 2000000); }
return 800000;
}
function estimateSlideH(slide) {
let h = (slide.title ? 1000000 : 0) + (slide.subtitle ? 800000 : 0);
for (const img of (slide.images || [])) {
const data = (img && typeof img === 'object') ? img : { data: img };
const imgW = data.width || (data.widthPct ? Math.floor(SLIDE_W * data.widthPct / 100) : (SLIDE_W - 2 * COL));
const imgH = data.height || (data.width && !data.height ? Math.floor(imgW * 9 / 16) : Math.floor(imgW * 9 / 16));
h += imgH + 100000;
}
for (const b of slide.blocks) { h += estimateBlockHeight(b); }
return h;
}
function buildSlideIR(ast) {
const slides = [];
let current = createSlide();
function createSlide() { return { title: '', subtitle: '', blocks: [], images: [], _overflow: false }; }
function splitOverflow() {
if (estimateSlideH(current) <= SLIDE_H) return;
const overflow = createSlide();
overflow._overflow = true;
let h = (current.title ? 1000000 : 0) + (current.subtitle ? 800000 : 0);
for (const img of (current.images || [])) {
const data = (img && typeof img === 'object') ? img : { data: img };
const imgW = data.width || (data.widthPct ? Math.floor(SLIDE_W * data.widthPct / 100) : (SLIDE_W - 2 * COL));
const imgH = data.height || Math.floor(imgW * 9 / 16);
h += imgH + 100000;
}
const keepBlocks = [], overflowBlocks = [];
for (const b of current.blocks) {
const bh = estimateBlockHeight(b);
if (h + bh <= SLIDE_H) { keepBlocks.push(b); h += bh; }
else { overflowBlocks.push(b); }
}
if (overflowBlocks.length > 0) { current.blocks = keepBlocks; overflow.blocks = overflowBlocks; slides.push(current); current = overflow; }
}
for (const b of ast.blocks) {
if (b.type === 'heading' && b.level === 1) {
if (current.title || current.blocks.length || current.images.length) { splitOverflow(); slides.push(current); }
current = createSlide(); current.title = b.text; continue;
}
if (b.type === 'slide_break') { splitOverflow(); slides.push(current); current = createSlide(); continue; }
if (b.type === 'heading' && b.level === 2) { current.subtitle = b.text; continue; }
if (b.type === 'image') {
// Store image as file path (NOT base64). Bytes read lazily by addImageToZip.
current.images.push({ data: b.data, _ext: b._ext, width: b.width, height: b.height, widthPct: b.widthPct });
continue;
}
current.blocks.push(b); splitOverflow();
}
splitOverflow(); slides.push(current); return slides;
}
// -- Layout Engine --
const COL = SLIDE_W / 12;
function grid(col, span, y, h) { return { x: col * COL, y, w: span * COL, h }; }
function layoutSlide(slide) {
const layout = [];
if (slide.title) { layout.push({ type: 'title', text: slide.title, rect: grid(0, 12, 0, 1000000) }); }
if (slide.subtitle) { layout.push({ type: 'subtitle', text: slide.subtitle, rect: grid(0, 12, 1000000, 800000) }); }
let bodyStart = slide.title ? (slide.subtitle ? 1800000 : 1000000) : (slide.subtitle ? 1800000 : 0);
const DEFAULT_IMG_W = SLIDE_W - 2 * COL;
const DEFAULT_IMG_H = Math.floor(DEFAULT_IMG_W * 9 / 16);
if (slide.images && slide.images.length > 0) {
for (const img of slide.images) {
const imgData = (img && typeof img === 'object') ? img : { data: img };
let imgW, imgH;
if (imgData.width && imgData.height) {
imgW = imgData.width;
imgH = imgData.height;
} else if (imgData.widthPct) {
imgW = Math.floor(SLIDE_W * imgData.widthPct / 100);
imgH = Math.floor(imgW * 9 / 16);
} else if (imgData.width && !imgData.height) {
imgW = imgData.width;
imgH = Math.floor(imgW * 9 / 16);
} else {
imgW = DEFAULT_IMG_W;
imgH = DEFAULT_IMG_H;
}
const imgX = Math.floor((SLIDE_W - imgW) / 2);
layout.push({ type: 'image', data: imgData.data, rect: { x: imgX, y: bodyStart, w: imgW, h: imgH } });
bodyStart += imgH + 100000;
}
}
let footerH = slide._showSlideNumber ? 600000 : 0;
if (footerH > 0) {
layout.push({ type: 'page_number', text: `${slide._pageNumber} / ${slide._totalPages}`, rect: grid(9, 3, SLIDE_H - footerH, 400000) });
}
let y = bodyStart;
for (const b of slide.blocks) {
if (b.type === 'paragraph') { const text = b.text || ''; const lines = Math.ceil(cjkWeightedLen(text) / CHARS_PER_LINE) || 1; const h = lines * LINE_HEIGHT; layout.push({ type: 'text', text: b.text, runs: b.runs, rect: grid(0, 12, y, h) }); y += h; }
if (b.type === 'list') { for (const item of b.items) { const lines = Math.ceil(cjkWeightedLen(item) / CHARS_PER_LINE) || 1; const h = lines * LINE_HEIGHT; layout.push({ type: 'bullet', text: item, rect: grid(1, 11, y, h) }); y += h; } }
if (b.type === 'ordered_list') { for (let idx = 0; idx < b.items.length; idx++) { const lines = Math.ceil(cjkWeightedLen(b.items[idx]) / CHARS_PER_LINE) || 1; const h = lines * LINE_HEIGHT; layout.push({ type: 'ordered_bullet', text: b.items[idx], number: idx + 1, rect: grid(1, 11, y, h) }); y += h; } }
if (b.type === 'blockquote') { for (const item of b.items) { const lines = Math.ceil(cjkWeightedLen(item) / CHARS_PER_LINE) || 1; const h = lines * LINE_HEIGHT; layout.push({ type: 'blockquote', text: item, rect: grid(1, 11, y, h) }); y += h; } }
if (b.type === 'code') { const codeLines = (b.code || '').split('\n').length; const h = Math.max(codeLines * 250000, 1500000); layout.push({ type: 'code', text: b.code, rect: grid(0, 12, y, h) }); y += h; }
if (b.type === 'table') { const rows = (b.rows || []).length || 1; const h = Math.max(rows * 400000, 2000000); layout.push({ type: 'table', rows: b.rows, rect: grid(0, 12, y, h) }); y += h; }
}
return layout;
}
// -- Image Embedding (Direct Binary Pass-Through) --
// DESIGN: Images flow through the pipeline as FILE PATHS (strings), NOT as
// base64 data URIs. File bytes are read ONLY at the final moment — directly
// from disk into the ZIP via addImageToZip(). This avoids ~33% base64 bloat
// and the corresponding memory/performance hit in Wasm environments when
// encoding many large PNG charts.
function resolveImages(ast) {
for (const block of ast.blocks) {
if (block.type === 'image') {
const filePath = block.data;
if (!filePath) { block.data = null; continue; }
if (filePath.startsWith('data:')) {
console.warn('WARNING: data URI image. Use direct file paths for better performance (avoid ~33% base64 bloat).');
continue;
}
if (filePath.startsWith('http://') || filePath.startsWith('https://')) {
console.warn(`WARNING: Image "${filePath}" is a remote URL. Skipping.`);
block.data = null; continue;
}
try {
const resolvedPath = pathModule.resolve(filePath);
fs.accessSync(resolvedPath);
const ext = pathModule.extname(filePath).toLowerCase();
block.data = resolvedPath;
block._ext = ext.replace('.', '') || 'png';
} catch (e) {
console.warn(`WARNING: Cannot access "${filePath}": ${e.message}. Skipping.`);
block.data = null;
}
}
}
ast.blocks = ast.blocks.filter(b => !(b.type === 'image' && b.data === null));
}
// Read image bytes from disk on demand — the ONLY place file bytes are read.
function addImageToZip(zip, imgPath, imgExt) {
const buf = fs.readFileSync(imgPath);
const ext = imgExt || 'png';
const name = nextImageName(ext);
zip.add(`ppt/media/${name}`, buf);
return { fileName: name, rId: nextRelId(), ext };
}
// -- Slide Rendering (OOXML Core) --
function slideMaster() {
return `<?xml version="1.0" encoding="UTF-8"?>
<p:sldMaster xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<p:cSld>
<p:bg>
<p:bgRef idx="1001">
<a:schemeClr val="bg1"/>
</p:bgRef>
</p:bg>
<p:spTree>
<p:nvGrpSpPr>
<p:cNvPr id="1" name=""/>
<p:cNvGrpSpPr/>
<p:nvPr/>
</p:nvGrpSpPr>
<p:grpSpPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="0" cy="0"/>
<a:chOff x="0" y="0"/>
<a:chExt cx="0" cy="0"/>
</a:xfrm>
</p:grpSpPr>
</p:spTree>
</p:cSld>
<p:clrMap bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/>
<p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
<p:txStyles>
<p:titleStyle>
<a:lvl1pPr algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPct val="0"/></a:spcBef>
<a:buNone/>
<a:defRPr sz="4400" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mj-lt"/>
<a:ea typeface="+mj-ea"/>
<a:cs typeface="+mj-cs"/>
</a:defRPr>
</a:lvl1pPr>
</p:titleStyle>
<p:bodyStyle>
<a:lvl1pPr marL="228600" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="1000"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="2800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl1pPr>
<a:lvl2pPr marL="685800" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="2400" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl2pPr>
<a:lvl3pPr marL="1143000" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="2000" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl3pPr>
<a:lvl4pPr marL="1600200" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl4pPr>
<a:lvl5pPr marL="2057400" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl5pPr>
<a:lvl6pPr marL="2514600" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl6pPr>
<a:lvl7pPr marL="2971800" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl7pPr>
<a:lvl8pPr marL="3429000" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl8pPr>
<a:lvl9pPr marL="3886200" indent="-228600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:lnSpc><a:spcPct val="90000"/></a:lnSpc>
<a:spcBef><a:spcPts val="500"/></a:spcBef>
<a:buFont typeface="Arial" panose="020B0604020202020204" pitchFamily="34" charset="0"/>
<a:buChar char="•"/>
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl9pPr>
</p:bodyStyle>
<p:otherStyle>
<a:defPPr>
<a:defRPr lang="en-US"/>
</a:defPPr>
<a:lvl1pPr marL="0" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl1pPr>
<a:lvl2pPr marL="457200" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl2pPr>
<a:lvl3pPr marL="914400" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl3pPr>
<a:lvl4pPr marL="1371600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl4pPr>
<a:lvl5pPr marL="1828800" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl5pPr>
<a:lvl6pPr marL="2286000" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl6pPr>
<a:lvl7pPr marL="2743200" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl7pPr>
<a:lvl8pPr marL="3200400" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl8pPr>
<a:lvl9pPr marL="3657600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl9pPr>
</p:otherStyle>
</p:txStyles>
</p:sldMaster>`;
}
function slideMasterRels() {
return `<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/>
</Relationships>`;
}
function slideLayout() {
return `<?xml version="1.0" encoding="UTF-8"?>
<p:sldLayout xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<p:cSld name="Default">
<p:spTree>
<p:nvGrpSpPr>
<p:cNvPr id="1" name=""/>
<p:cNvGrpSpPr/>
<p:nvPr/>
</p:nvGrpSpPr>
<p:grpSpPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="0" cy="0"/>
<a:chOff x="0" y="0"/>
<a:chExt cx="0" cy="0"/>
</a:xfrm>
</p:grpSpPr>
</p:spTree>
</p:cSld>
<p:clrMapOvr>
<a:masterClrMapping/>
</p:clrMapOvr>
</p:sldLayout>`;
}
function slideLayoutRels() {
return `<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
</Relationships>`;
}
function theme() {
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">
<a:themeElements>
<a:clrScheme name="Office">
<a:dk1><a:srgbClr val="000000"/></a:dk1>
<a:lt1><a:srgbClr val="FFFFFF"/></a:lt1>
<a:dk2><a:srgbClr val="44546A"/></a:dk2>
<a:lt2><a:srgbClr val="E7E6E6"/></a:lt2>
<a:accent1><a:srgbClr val="4472C4"/></a:accent1>
<a:accent2><a:srgbClr val="ED7D31"/></a:accent2>
<a:accent3><a:srgbClr val="A5A5A5"/></a:accent3>
<a:accent4><a:srgbClr val="FFC000"/></a:accent4>
<a:accent5><a:srgbClr val="5B9BD5"/></a:accent5>
<a:accent6><a:srgbClr val="70AD47"/></a:accent6>
<a:hlink><a:srgbClr val="0563C1"/></a:hlink>
<a:folHlink><a:srgbClr val="954F72"/></a:folHlink>
</a:clrScheme>
<a:fontScheme name="Office">
<a:majorFont><a:latin typeface="Calibri Light"/><a:ea typeface="Microsoft YaHei Light"/><a:cs typeface="Calibri Light"/></a:majorFont>
<a:minorFont><a:latin typeface="Calibri"/><a:ea typeface="Microsoft YaHei"/><a:cs typeface="Calibri"/></a:minorFont>
</a:fontScheme>
<a:fmtScheme name="Office">
<a:fillStyleLst>
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="50000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
<a:gs pos="35000"><a:schemeClr val="phClr"><a:tint val="37000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
<a:gs pos="100000"><a:schemeClr val="phClr"><a:tint val="15000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
</a:gsLst>
<a:lin ang="16200000" scaled="1"/>
</a:gradFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0"><a:schemeClr val="phClr"><a:shade val="51000"/><a:satMod val="130000"/></a:schemeClr></a:gs>
<a:gs pos="80000"><a:schemeClr val="phClr"><a:shade val="93000"/><a:satMod val="130000"/></a:schemeClr></a:gs>
<a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="94000"/><a:satMod val="135000"/></a:schemeClr></a:gs>
</a:gsLst>
<a:lin ang="16200000" scaled="0"/>
</a:gradFill>
</a:fillStyleLst>
<a:lnStyleLst>
<a:ln w="9525" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"><a:shade val="95000"/><a:satMod val="105000"/></a:schemeClr></a:solidFill><a:prstDash val="solid"/></a:ln>
<a:ln w="25400" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
<a:ln w="38100" cap="flat" cmpd="sng" algn="ctr"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill><a:prstDash val="solid"/></a:ln>
</a:lnStyleLst>
<a:effectStyleLst>
<a:effectStyle><a:effectLst/></a:effectStyle>
<a:effectStyle><a:effectLst/></a:effectStyle>
<a:effectStyle><a:effectLst/></a:effectStyle>
</a:effectStyleLst>
<a:bgFillStyleLst>
<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="40000"/><a:satMod val="350000"/></a:schemeClr></a:gs>
<a:gs pos="40000"><a:schemeClr val="phClr"><a:tint val="45000"/><a:satMod val="350000"/><a:shade val="99000"/></a:schemeClr></a:gs>
<a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="20000"/><a:satMod val="255000"/></a:schemeClr></a:gs>
</a:gsLst>
<a:path path="circle"><a:fillToRect l="50000" t="-80000" r="50000" b="180000"/></a:path>
</a:gradFill>
<a:gradFill rotWithShape="1">
<a:gsLst>
<a:gs pos="0"><a:schemeClr val="phClr"><a:tint val="80000"/><a:satMod val="300000"/></a:schemeClr></a:gs>
<a:gs pos="100000"><a:schemeClr val="phClr"><a:shade val="30000"/><a:satMod val="200000"/></a:schemeClr></a:gs>
</a:gsLst>
<a:path path="circle"><a:fillToRect l="50000" t="50000" r="50000" b="50000"/></a:path>
</a:gradFill>
</a:bgFillStyleLst>
</a:fmtScheme>
</a:themeElements>
<a:objectDefaults/>
<a:extraClrSchemeLst/>
</a:theme>`;
}
function renderRuns(runs, baseFontSize) {
let body = '';
for (const run of runs) {
let rPrAttrs = baseFontSize ? ` sz="${baseFontSize}"` : '';
let rPrChildren = '';
let needsClose = false;
if (run.bold) rPrAttrs += ' b="1"';
if (run.italic) rPrAttrs += ' i="1"';
if (run.mono) { rPrChildren += `<a:solidFill><a:srgbClr val="C7254E"/></a:solidFill><a:latin typeface="Consolas"/>`; needsClose = true; }
if (run.link) {
const linkRId = run._linkRId || '';
rPrAttrs += ' u="sng"';
rPrChildren += `<a:solidFill><a:srgbClr val="0563C1"/></a:solidFill><a:uLn><a:solidFill><a:srgbClr val="0563C1"/></a:solidFill></a:uLn><a:latin typeface="Calibri"/>`;
rPrChildren += `<a:hlinkClick r:id="${linkRId}" tooltip="${esc(run.link)}"/>`;
needsClose = true;
}
const rPrOpen = needsClose ? `<a:rPr${rPrAttrs}>${rPrChildren}` : `<a:rPr${rPrAttrs}/>`;
const rPrClose = needsClose ? `</a:rPr>` : '';
const runBody = `<a:t>${esc(run.text)}</a:t>`;
body += `<a:r>${rPrOpen}${rPrClose}${runBody}</a:r>`;
}
return body;
}
function scanAndAssignLinks(slide, layout) {
slide._linkMap = {};
for (const item of layout) {
const runs = item.runs || (item.text ? parseInlineRuns(item.text) : []);
for (const run of runs) { if (run.link && !slide._linkMap[run.link]) { slide._linkMap[run.link] = nextRelId(); } }
}
for (const item of layout) { if (item.runs) { for (const run of item.runs) { if (run.link) run._linkRId = slide._linkMap[run.link]; } } }
}
function renderSlideXML(slide, layout) {
let shapes = '';
for (const item of layout) {
if (item.type === 'title' || item.type === 'subtitle' || item.type === 'text' || item.type === 'bullet') {
const r = item.rect;
const fontSize = item.type === 'title' ? 3600 : 1800;
const runs = item.runs || parseInlineRuns(item.text);
const isBullet = item.type === 'bullet';
const bulletChar = isBullet ? `<a:buChar char="\u2022"/>` : '';
const runsXml = renderRuns(runs, fontSize);
shapes += `
<p:sp>
<p:nvSpPr>
<p:cNvPr id="${nextShapeId()}" name="${item.type}"/>
<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</a:xfrm>
</p:spPr>
<p:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p><a:pPr>${bulletChar}</a:pPr>${runsXml}</a:p>
</p:txBody>
</p:sp>`;
}
if (item.type === 'page_number') {
const r = item.rect;
shapes += `
<p:sp>
<p:nvSpPr>
<p:cNvPr id="${nextShapeId()}" name="pageNumber"/>
<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</a:xfrm>
</p:spPr>
<p:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p><a:pPr algn="r"/><a:r><a:rPr sz="1200"><a:solidFill><a:srgbClr val="888888"/></a:solidFill></a:rPr><a:t>${esc(item.text)}</a:t></a:r></a:p>
</p:txBody>
</p:sp>`;
}
if (item.type === 'ordered_bullet') {
const r = item.rect;
const runsXml = renderRuns(parseInlineRuns(item.text), 1800);
shapes += `
<p:sp>
<p:nvSpPr>
<p:cNvPr id="${nextShapeId()}" name="ordered"/>
<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</a:xfrm>
</p:spPr>
<p:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p><a:pPr><a:buAutoNum type="arabicPeriod"/></a:pPr>${runsXml}</a:p>
</p:txBody>
</p:sp>`;
}
if (item.type === 'blockquote') {
const r = item.rect;
const runs = parseInlineRuns(item.text);
// Note: blockquote no longer forces italic — preserves user formatting intent
const runsXml = renderRuns(runs, 1600);
shapes += `
<p:sp>
<p:nvSpPr>
<p:cNvPr id="${nextShapeId()}" name="blockquote"/>
<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</a:xfrm>
<a:ln w="25400">
<a:solidFill><a:srgbClr val="4472C4"/></a:solidFill>
</a:ln>
</p:spPr>
<p:txBody>
<a:bodyPr lIns="228600"/>
<a:lstStyle/>
<a:p>${runsXml}</a:p>
</p:txBody>
</p:sp>`;
}
if (item.type === 'code') {
const r = item.rect;
const lines = item.text.split('\n');
let paragraphs = '';
for (const line of lines) {
paragraphs += `<a:p><a:r><a:rPr sz="1200"><a:solidFill><a:srgbClr val="333333"/></a:solidFill></a:rPr><a:t>${esc(line)}</a:t></a:r></a:p>`;
}
shapes += `
<p:sp>
<p:nvSpPr>
<p:cNvPr id="${nextShapeId()}" name="code"/>
<p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr>
<p:nvPr/>
</p:nvSpPr>
<p:spPr>
<a:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</a:xfrm>
<a:solidFill>
<a:srgbClr val="F5F5F5"/>
</a:solidFill>
</p:spPr>
<p:txBody>
<a:bodyPr lIns="91440" tIns="45720" rIns="91440" bIns="45720"/>
<a:lstStyle/>
${paragraphs}
</p:txBody>
</p:sp>`;
}
if (item.type === 'table') {
const r = item.rect;
const rows = item.rows;
const numCols = rows[0] ? rows[0].length : 1;
const colWidth = Math.floor(r.w / numCols);
const rowHeight = Math.floor(r.h / rows.length);
let gridCols = '';
for (let c = 0; c < numCols; c++) { gridCols += `<a:gridCol w="${colWidth}"/>`; }
let tableRows = '';
for (let ri = 0; ri < rows.length; ri++) {
let cells = '';
const isHeader = ri === 0;
for (let ci = 0; ci < (rows[ri] ? rows[ri].length : 0); ci++) {
if (isHeader) {
cells += `<a:tc>
<a:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p><a:r><a:rPr sz="1400" b="1"><a:solidFill><a:srgbClr val="FFFFFF"/></a:solidFill></a:rPr><a:t>${esc(rows[ri][ci])}</a:t></a:r></a:p>
</a:txBody>
<a:tcPr><a:solidFill><a:srgbClr val="4472C4"/></a:solidFill></a:tcPr>
</a:tc>`;
} else {
cells += `<a:tc>
<a:txBody>
<a:bodyPr/>
<a:lstStyle/>
<a:p><a:r><a:rPr sz="1400"/><a:t>${esc(rows[ri][ci])}</a:t></a:r></a:p>
</a:txBody>
<a:tcPr/>
</a:tc>`;
}
}
tableRows += `<a:tr h="${rowHeight}">${cells}</a:tr>`;
}
shapes += `
<p:graphicFrame>
<p:nvGraphicFramePr>
<p:cNvPr id="${nextShapeId()}" name="table"/>
<p:cNvGraphicFramePr><a:graphicFrameLocks noGrp="1"/></p:cNvGraphicFramePr>
<p:nvPr/>
</p:nvGraphicFramePr>
<p:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</p:xfrm>
<a:graphic>
<a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/table">
<a:tbl>
<a:tblPr/>
<a:tblGrid>${gridCols}</a:tblGrid>
${tableRows}
</a:tbl>
</a:graphicData>
</a:graphic>
</p:graphicFrame>`;
}
if (item.type === 'image' && item.imageRId) {
const r = item.rect;
shapes += `
<p:pic>
<p:nvPicPr>
<p:cNvPr id="${nextShapeId()}" name="image"/>
<p:cNvPicPr/>
<p:nvPr/>
</p:nvPicPr>
<p:blipFill>
<a:blip r:embed="${item.imageRId}"/>
<a:stretch>
<a:fillRect/>
</a:stretch>
</p:blipFill>
<p:spPr>
<a:xfrm>
<a:off x="${r.x}" y="${r.y}"/>
<a:ext cx="${r.w}" cy="${r.h}"/>
</a:xfrm>
<a:prstGeom prst="rect">
<a:avLst/>
</a:prstGeom>
</p:spPr>
</p:pic>`;
}
}
return `<?xml version="1.0" encoding="UTF-8"?>
<p:sld xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<p:cSld>
<p:spTree>
<p:nvGrpSpPr>
<p:cNvPr id="1" name=""/>
<p:cNvGrpSpPr/>
<p:nvPr/>
</p:nvGrpSpPr>
<p:grpSpPr>
<a:xfrm>
<a:off x="0" y="0"/>
<a:ext cx="0" cy="0"/>
<a:chOff x="0" y="0"/>
<a:chExt cx="0" cy="0"/>
</a:xfrm>
</p:grpSpPr>
${shapes}
</p:spTree>
</p:cSld>
<p:clrMapOvr>
<a:masterClrMapping/>
</p:clrMapOvr>
</p:sld>`;
}
// -- OOXML Template Functions --
function rootRels() {
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/></Relationships>`;
}
function presentation(slideCount) {
let slideList = '';
for (let i = 0; i < slideCount; i++) { slideList += ` <p:sldId id="${256 + i}" r:id="rId${i + 2}"/>\n`; }
return `<?xml version="1.0" encoding="UTF-8"?>
<p:presentation xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<p:sldMasterIdLst>
<p:sldMasterId id="2147483648" r:id="rId1"/>
</p:sldMasterIdLst>
<p:sldIdLst>
${slideList} </p:sldIdLst>
<p:sldSz cx="9144000" cy="6858000" type="screen4x3"/>
<p:notesSz cx="6858000" cy="9144000"/>
<p:defaultTextStyle>
<a:defPPr>
<a:defRPr lang="en-US"/>
</a:defPPr>
<a:lvl1pPr marL="0" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl1pPr>
<a:lvl2pPr marL="457200" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl2pPr>
<a:lvl3pPr marL="914400" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl3pPr>
<a:lvl4pPr marL="1371600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl4pPr>
<a:lvl5pPr marL="1828800" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl5pPr>
<a:lvl6pPr marL="2286000" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl6pPr>
<a:lvl7pPr marL="2743200" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl7pPr>
<a:lvl8pPr marL="3200400" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl8pPr>
<a:lvl9pPr marL="3657600" algn="l" defTabSz="914400" rtl="0" eaLnBrk="1" latinLnBrk="0" hangingPunct="1">
<a:defRPr sz="1800" kern="1200">
<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>
<a:latin typeface="+mn-lt"/>
<a:ea typeface="+mn-ea"/>
<a:cs typeface="+mn-cs"/>
</a:defRPr>
</a:lvl9pPr>
</p:defaultTextStyle>
</p:presentation>`;
}
function presentationRels(slideCount) {
let rels = ` <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>\n`;
for (let i = 0; i < slideCount; i++) { rels += ` <Relationship Id="rId${i + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide${i + 1}.xml"/>\n`; }
const presRId = slideCount + 2;
rels += ` <Relationship Id="rId${presRId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps" Target="presProps.xml"/>\n`;
rels += ` <Relationship Id="rId${presRId + 1}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps" Target="viewProps.xml"/>\n`;
rels += ` <Relationship Id="rId${presRId + 2}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/>\n`;
rels += ` <Relationship Id="rId${presRId + 3}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles" Target="tableStyles.xml"/>\n`;
return `<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
${rels}</Relationships>`;
}
function slideRels(slide) {
let rels = ` <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>\n`;
if (slide._images) { for (const img of slide._images) { rels += ` <Relationship Id="${img.rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="../media/${img.fileName}"/>\n`; } }
if (slide._linkMap) { for (const [url, rId] of Object.entries(slide._linkMap)) { rels += ` <Relationship Id="${rId}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="${esc(url)}" TargetMode="External"/>\n`; } }
return `<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
${rels}</Relationships>`;
}
function contentTypes(slideCount, imageExts) {
let defaults = ` <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>\n`;
defaults += ` <Default Extension="xml" ContentType="application/xml"/>\n`;
if (imageExts && imageExts.size > 0) {
for (const ext of imageExts) {
defaults += ` <Default Extension="${ext}" ContentType="image/${ext}"/>\n`;
}
}
let overrides = '';
overrides += ` <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>\n`;
overrides += ` <Override PartName="/ppt/presProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"/>\n`;
overrides += ` <Override PartName="/ppt/viewProps.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml"/>\n`;
overrides += ` <Override PartName="/ppt/tableStyles.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml"/>\n`;
overrides += ` <Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>\n`;
overrides += ` <Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>\n`;
overrides += ` <Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>\n`;
for (let i = 0; i < slideCount; i++) { overrides += ` <Override PartName="/ppt/slides/slide${i + 1}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>\n`; }
overrides += ` <Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>\n`;
overrides += ` <Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>\n`;
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
${defaults}${overrides}</Types>`;
}
function presProps() { return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p:presentationPr xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<p:extLst>
<p:ext uri="{E76CE94A-603C-4142-B9EB-6D1370010A27}">
<p14:discardImageEditData xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="0"/>
</p:ext>
<p:ext uri="{D31A062A-798A-432F-ABDD-D45BAFD82E40}">
<p14:defaultImageDpi xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="220"/>
</p:ext>
<p:ext uri="{FD5C536A-5214-4C46-B8D8-A1DB69A19C07}">
<p14:chartTrackingRefBased xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="1"/>
</p:ext>
</p:extLst>
</p:presentationPr>`; }
function viewProps() {
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p:viewPr xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"
xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<p:normalViewPr showOutlineIcons="1">
<p:restoredLeft sz="15600"/>
<p:restoredTop sz="15260"/>
</p:normalViewPr>
<p:slideViewPr showComments="1">
<p:cSldViewPr snapToGrid="1" snapToObjects="1" showGuides="1">
<p:cViewPr varScale="1">
<p:scale>
<a:sx n="100000" d="100000"/>
<a:sy n="100000" d="100000"/>
</p:scale>
<p:origin x="0" y="0"/>
</p:cViewPr>
<p:guideLst/>
</p:cSldViewPr>
</p:slideViewPr>
<p:notesTextViewPr>
<p:cViewPr>
<p:scale>
<a:sx n="1" d="1"/>
<a:sy n="1" d="1"/>
</p:scale>
<p:origin x="0" y="0"/>
</p:cViewPr>
</p:notesTextViewPr>
<p:gridSpacing cx="76200" cy="76200"/>
</p:viewPr>`;
}
function tableStyles() { return `<?xml version="1.0" encoding="UTF-8"?>\n<a:tblStyleLst xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" def="{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"/>`; }
function docPropsCore() {
const now = new Date().toISOString().replace(/\.\d{3}/, '');
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:creator>BoxAgnts</dc:creator><cp:lastModifiedBy>BoxAgnts</cp:lastModifiedBy><cp:revision>1</cp:revision><dcterms:modified xsi:type="dcterms:W3CDTF">${now}</dcterms:modified></cp:coreProperties>`;
}
function docPropsApp(slideCount) {
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><TotalTime>0</TotalTime><Words>0</Words><Application>BoxAgnts PPTX Generator</Application><PresentationFormat>On-screen Show (4:3)</PresentationFormat><Paragraphs>0</Paragraphs><Slides>${slideCount}</Slides><Notes>0</Notes><HiddenSlides>0</HiddenSlides><MMClips>0</MMClips><ScaleCrop>false</ScaleCrop><LinksUpToDate>false</LinksUpToDate><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>1.0</AppVersion></Properties>`;
}
// -- Main Compiler Pipeline --
function compileMarkdownToPPTX(md, output_path, options) {
if (!options) options = {};
shapeId = 1; relId = 2; imageIndex = 1;
const zip = new ZipBuilder();
const ast = parseMarkdownAST(md);
resolveImages(ast);
const slides = buildSlideIR(ast);
const totalSlides = slides.length;
const imageExts = new Set();
for (let i = 0; i < slides.length; i++) {
const slide = slides[i];
slide._images = [];
slide._showSlideNumber = !!options.showSlideNumbers;
slide._pageNumber = i + 1;
slide._totalPages = totalSlides;
if (slide.images && slide.images.length > 0) {
for (const img of slide.images) {
const imgData = (img && typeof img === 'object') ? img.data : img;
if (imgData && typeof imgData === 'string' && !imgData.startsWith('data:')) {
const ext = (img && img._ext) || 'png';
const imgInfo = addImageToZip(zip, imgData, ext);
slide._images.push(imgInfo);
if (imgInfo.ext) imageExts.add(imgInfo.ext);
}
}
}
}
zip.add("_rels/.rels", rootRels());
zip.add("ppt/presentation.xml", presentation(slides.length));
zip.add("ppt/_rels/presentation.xml.rels", presentationRels(slides.length));
zip.add("ppt/presProps.xml", presProps());
zip.add("ppt/viewProps.xml", viewProps());
zip.add("ppt/tableStyles.xml", tableStyles());
zip.add("ppt/slideMasters/slideMaster1.xml", slideMaster());
zip.add("ppt/slideMasters/_rels/slideMaster1.xml.rels", slideMasterRels());
zip.add("ppt/slideLayouts/slideLayout1.xml", slideLayout());
zip.add("ppt/slideLayouts/_rels/slideLayout1.xml.rels", slideLayoutRels());
zip.add("ppt/theme/theme1.xml", theme());
zip.add("docProps/core.xml", docPropsCore());
zip.add("docProps/app.xml", docPropsApp(slides.length));
for (let i = 0; i < slides.length; i++) {
const slide = slides[i];
const layout = layoutSlide(slide);
let imgIdx = 0;
for (const item of layout) { if (item.type === 'image' && slide._images[imgIdx]) { item.imageRId = slide._images[imgIdx].rId; imgIdx++; } }
scanAndAssignLinks(slide, layout);
zip.add(`ppt/slides/slide${i + 1}.xml`, renderSlideXML(slide, layout));
zip.add(`ppt/slides/_rels/slide${i + 1}.xml.rels`, slideRels(slide));
}
zip.add("[Content_Types].xml", contentTypes(slides.length, imageExts));
fs.writeFileSync(output_path, zip.build());
return { path: output_path, slides: slides.length };
}
// -- ENTRY POINT - DO NOT MODIFY --
const md = `{{markdown}}`.replace(/^\{\{markdown\}\}$/, '') || '';
const out = `{{output_path}}`.replace(/^\{\{output_path\}\}$/, '') || '/tmp/output.pptx';
const showNums = (`{{show_slide_numbers}}`.replace(/^\{\{show_slide_numbers\}\}$/, '') || 'false') === 'true';
const result = compileMarkdownToPPTX(md, out, { showSlideNumbers: showNums });
console.log(`PPTX generated: ${result.path}`);
console.log(`Slides: ${result.slides}`);
})();
What this file has done since we first saw it
Hashed on every crawl. A supply-chain change to an agent config is a question of when, not whether, so the history is kept rather than the latest state alone.
- 5d ago First seen · 1,510 lines · 45 tokens per session scan A d11b9df5ef7e
pptx-generator is a skill published in the GitHub repository guyoung/boxagnts (11 stars, last pushed 1mo ago), licensed MIT. It adds 45 tokens to every session and 21,925 once invoked, about $0.0002 per session on Opus 5. A static security scan graded it A with 0 findings. No closer match exists in the catalogue, so it is treated as the original; first seen 2026-08-30.
Other skills, from other repositories
pptx
当涉及到 .pptx 文件的任何操作时使用此技能——无论是作为输入、输出还是两者兼有。包括:创建幻灯片、演示文稿或路演材料;读取、解析或提取任何 .pptx 文件中的文本(即使提取的内容将用于其他地方,如邮件或摘要);编辑、修改或更新现有演示文稿;合并或拆分幻灯片文件;处理模板、布局、演讲者备注或批注。当用户提到“演示文稿”、”幻灯片“、”PPT“或引用 .pptx 文件名时触发,无论他们之后打算如何使用内容。如果需要打开、创建或操作 .pptx 文件,就使用此技能。.
当用户需要对PDF文件进行任何操作时,请使用此技能。包括从 PDF 中读取或提取文本/表格、合并多个 PDF、拆分 PDF、旋转页面、添加水印、创建新PDF、填写PDF表单、加密/解密 PDF、提取图片,以及对扫描版 PDF 进行 OCR 使其可搜索。如果用户提到 .pdf 文件或要求生成 PDF,请使用此技能。.
nano-pdf
Edits PDF files using natural-language instructions via the nano-pdf CLI. Supports modifying text, changing titles, fixing typos, and updating content on specific pages. Use when the user wants to edit a PDF, modify PDF content, update PDF text, fix a typo in a PDF, change a PDF title, or rewrite part of a PDF page.
pdf-toolkit
Structured .pdf operations: extract text/tables, merge pages from multiple PDFs, split a PDF by page ranges, fill PDF form fields, and generate fresh PDFs from JSON. Trigger when the user wants programmatic PDF work without natural-language rewriting — examples: pull tables from a report, combine three PDFs, extract…
hive.pdf
Read, write, merge, split, rotate, watermark, encrypt, and OCR PDF files using Python (pypdf, pdfplumber, reportlab, pypdfium2) and command-line tools (poppler-utils, qpdf). Use when the user asks to extract text/tables/images from a PDF, create or modify a PDF, combine or split PDFs, OCR a scanned PDF…
sn-ppt-entry
Entry point for PPT generation. Asks the user to choose a mode (fast, standard, or creative), then collects role / audience / scene / pagecount as needed. For standard mode, also asks how images should be sourced (AI generation, web search, or none), whether charts should use AI-generated infographics or ECharts, and…