From Presentations
Parses PPTX files by extracting slides, shapes, text, and images with position data, outputting JSON for HTML reconstruction.
How this skill is triggered — by the user, by Claude, or both
Slash command
/presentations:pptx-importWhen to use
Юзер прислал .pptx, нужно либо посмотреть содержимое, либо переверстать в HTML.
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
PPTX — обычный zip с XML внутри. Структура:
PPTX — обычный zip с XML внутри. Структура:
deck.pptx (zip)
├── ppt/
│ ├── slides/
│ │ ├── slide1.xml
│ │ ├── slide2.xml
│ ├── media/
│ │ ├── image1.png
│ ├── theme/theme1.xml
│ └── presentation.xml
└── [Content_Types].xml
Можно через python-pptx (надёжнее) или собственный парсер на Node.
pip install python-pptx
templates/import.py:
#!/usr/bin/env python3
import sys, json, os, base64
from pptx import Presentation
from pptx.util import Emu
EMU_PER_PX = 9525
def emu_px(emu): return int(round(emu / EMU_PER_PX))
def shape_to_dict(shape):
out = {
'kind': str(shape.shape_type).split('.')[-1] if shape.shape_type else 'unknown',
'x': emu_px(shape.left or 0),
'y': emu_px(shape.top or 0),
'w': emu_px(shape.width or 0),
'h': emu_px(shape.height or 0),
}
if shape.has_text_frame:
out['kind'] = 'text'
out['paragraphs'] = []
for p in shape.text_frame.paragraphs:
runs = []
for r in p.runs:
runs.append({
'text': r.text,
'bold': r.font.bold,
'italic': r.font.italic,
'size': r.font.size.pt if r.font.size else None,
'name': r.font.name,
'color': str(r.font.color.rgb) if r.font.color and r.font.color.type else None,
})
out['paragraphs'].append({ 'runs': runs, 'align': str(p.alignment) if p.alignment else None })
elif shape.shape_type == 13: # PICTURE
out['kind'] = 'image'
try:
img = shape.image
out['ext'] = img.ext
out['data_b64'] = base64.b64encode(img.blob).decode()
except Exception: pass
return out
def main():
if len(sys.argv) < 2:
print("Usage: python import.py <file.pptx> [out.json]"); sys.exit(1)
src = sys.argv[1]
out = sys.argv[2] if len(sys.argv) > 2 else src.replace('.pptx', '.json')
prs = Presentation(src)
W, H = emu_px(prs.slide_width), emu_px(prs.slide_height)
slides = []
for i, sl in enumerate(prs.slides):
items = [shape_to_dict(sh) for sh in sl.shapes]
slides.append({ 'index': i, 'items': items })
with open(out, 'w') as f:
json.dump({ 'width': W, 'height': H, 'slides': slides }, f, indent=2, default=str)
print(f"✓ {out} — {len(slides)} slides, {W}×{H}")
if __name__ == "__main__":
main()
python import.py deck.pptx
# → deck.json
templates/json-to-html.mjs:
import fs from 'node:fs/promises';
const file = process.argv[2];
const data = JSON.parse(await fs.readFile(file, 'utf8'));
let html = `<!doctype html><html><head><meta charset="utf-8"><title>Imported</title>
<script src="deck-stage.js"></script>
<style>
body { margin: 0; }
.slide-content { position: absolute; }
</style></head><body><deck-stage>`;
for (const sl of data.slides) {
html += `\n<section data-screen-label="${String(sl.index+1).padStart(2,'0')}">`;
for (const it of sl.items) {
const style = `left:${it.x}px;top:${it.y}px;width:${it.w}px;height:${it.h}px;`;
if (it.kind === 'text') {
const txt = (it.paragraphs || []).map(p =>
p.runs.map(r => {
let t = `<span`;
if (r.size) t += ` style="font-size:${r.size}px;`;
if (r.bold) t += `font-weight:bold;`;
if (r.color) t += `color:#${r.color};`;
t += `"`;
return t + `>${r.text}</span>`;
}).join('')
).join('<br>');
html += `\n<div class="slide-content" style="${style}">${txt}</div>`;
} else if (it.kind === 'image' && it.data_b64) {
html += `\n<img class="slide-content" src="data:image/${it.ext};base64,${it.data_b64}" style="${style}">`;
} else {
html += `\n<div class="slide-content" style="${style};outline:1px dashed #aaa;"></div>`;
}
}
html += `\n</section>`;
}
html += `\n</deck-stage></body></html>`;
await fs.writeFile(file.replace('.json', '.html'), html);
console.log('✓', file.replace('.json', '.html'));
python import.py original.pptx → original.jsonnode json-to-html.mjs original.json → original.htmlpptx-editable-extractor → новый PPTX.Если PPTX сделан в дизайнерском стиле (выровнено по сетке, аккуратные шрифты) — round-trip даёт 80% результат, доделать вручную ~20%.
Если PPTX-каша (Word-style, всё в textbox'ах с разными шрифтами) — лучше переписать с нуля в HTML, импорт даёт текст и картинки, остальное — заново.
pptx-editable-extractor — обратная задача (HTML → PPTX).slides — каркас для нового HTML, куда переносить контент.document-import — общий импорт docx/pdf/pptx.npx claudepluginhub jhamidun/claude-code-config-pack --plugin presentationsBuilds a throwaway prototype to answer a design question about UI appearance or state/logic behavior. Guides you through two branches: interactive terminal app for logic validation, or multiple UI variations for visual exploration.