paperbanana-slide-deck について
PaperBanana のスライドデッキを一貫して作成・変更するスキルで、画像モードと編集可能モードを明示的に選べます。画像モードはスライド全体の画像を生成して PPTX にまとめ、編集可能モードは構造化仕様からテキスト、表、グラフ、図形を作成します。
主な機能
- 01画像または編集可能モードの明示的な選択
- 02スタイル調査とアウトライン、プロンプトの作成
- 03画像生成と途中バッチからの再開
- 04スライド画像の PPTX への統合
- 05編集可能な PPTX の構築、プレビュー、レビュー
活用シーン
- 01テーマと内容から新しいデッキを作成
- 02プロンプト編集後に既存デッキを再生成
- 03PowerPoint で編集できる要素を含む PPTX の作成
- 04プレビューを確認してスライドデザインを反復改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ plutolei/ paperbanana-skill --skill paperbanana-slide-deckインストール後、Claude Code で/paperbanana-slide-deckを入力して読み込まれたか確認します。
詳細
PaperBanana スライドデッキ
スライドデッキの作成と変更をエンドツーエンドで行います。デッキのモードは image と editable の2種類があり、フェーズ M で明示的に選択します。image モードには、新しいデッキを一から作成するワークフローと、プロンプトを編集した後に既存のデッキを再生成するワークフローがあります。
画像パイプライン: コンテンツ → スタイル選択 → アウトライン → プロンプト → 画像生成 → PPTX の結合
編集可能パイプライン: コンテンツ → slide-spec.json(ネイティブのテキスト/表/グラフ/図形/線/グループ)→ 検証 → PPTX の構築 → プレビューのレンダリング → Critic によるレビュー → 仕様の修正 → 再構築
フェーズ M:デッキモードの選択
deck-mode.txt に image または editable のいずれかを、行に1つだけ記録します。
- PowerPoint でテキスト/表/グラフ/コネクタを編集したい、または編集可能な PPTX を明示的に希望している場合は、
editableを使用します。 - 生成したスライド全体のアートワークを希望している場合、または既存の PaperBanana の画像ワークフローを維持する場合は、
imageを使用します。 - どちらも適切な可能性がある場合は、生成前に確認してください。ファイルの有無から推測してはいけません。
editableにはslide-spec.jsonが必要です。仕様がない、または無効な場合はエラーです。編集可能だと主張しながら、image モードにフォールバックしてはいけません。
モードの判定(image モード)
image モードでは、対象ディレクトリを確認してワークフローを判定します。
| 条件 | モード | 開始フェーズ |
|---|---|---|
対象ディレクトリが存在しない、または outline.md がない | 新規作成 | フェーズ R(RDIV 全体) |
対象ディレクトリに outline.md と prompts/ がある | 変更 | フェーズ I(生成と結合) |
変更モードでは、ユーザーがすでに外部でプロンプトまたはアウトラインを編集しています。フェーズ I に直接進みます。
依存関係
| コンポーネント | 役割 | 場所 |
|---|---|---|
google-genai SDK | パス B の画像生成 | pip install google-genai |
| PaperBanana CLI | パス A の画像生成 | python -m paperbanana.cli slide-batch |
| merge-to-pptx.ts | PPTX の結合(image モード) | plugins/paperbanana-slide-deck/scripts/merge-to-pptx.ts |
| build-deck.ts | 明示的な --mode image|editable ルーター | plugins/paperbanana-slide-deck/scripts/build-deck.ts |
| 編集可能レンダラー | slide-spec.json → ネイティブ PPTX | plugins/paperbanana-slide-deck/scripts/editable/ |
| スタイルライブラリ | スタイルプリセット(4つのソース、150以上) | 検出パスは R1 を参照 |
| Bun | ビルド/結合スクリプトの実行 | bun |
編集可能モードのワークフロー
deck-mode.txt が editable の場合は、以下のループに従い、フェーズ R/D/I はスキップします(仕様の詳細と完全な例については、references/editable-slide-spec.md を参照してください)。
- テキストを含まないアセットのみを生成します(写真、イラストパネルなど。レンダリング済みテキストは含めません)。PaperBanana を使用し、各アセットの SHA-256 を記録します。
slide-deck/{topic-slug}/slide-spec.jsonを作成します。13.333×7.5 インチのキャンバスにネイティブのtext、shape、line、table、chart、image、group要素を配置し、各スライドのスピーカーノートに[Sources]を含めます。- 検証してビルドします(フェイルクローズ方式。仕様がない、または無効な場合はエラーとし、image モードへ暗黙にフォールバックしません)。
bun "${SKILL_DIR}/scripts/build-deck.ts" --mode editable "slide-deck/{topic-slug}"
- LibreOffice と Poppler を使って PPTX のプレビューをレンダリングします(
soffice --headless --convert-to pdfを実行し、続けてpdftoppm -png -r 150 … slideを実行してslide-NN.pngを生成します)。 - プレビューに対して Critic を実行します(プレビューのみを対象とし、PPTX は編集しません)。
python3 "${SKILL_DIR}/scripts/editable/review-editable.py" \
--spec "slide-deck/{topic-slug}/slide-spec.json" \
--rendered-dir "slide-deck/{topic-slug}/rendered" \
--output "slide-deck/{topic-slug}/critic-review.json"
- 検出結果を要素 ID に対応付けます(提案は
slide-id/element-id: …の形式で返されます)。slide-spec.jsonを修正して再ビルドします。問題がなくなるまで繰り返します。
Phase R:リサーチ(新規モードのみ)
Modify モードではこのフェーズを完全にスキップします。
R1:スタイルライブラリを見つける — 4 つのソースすべてをスキャンし、統合リストにまとめます。
# Source 0 (PRIMARY): Local extended library (123 styles from Fooocus/ComfyUI, structured)
LOCAL_STYLES="$(find ~/.claude/skills .claude/skills -maxdepth 1 -name 'paperbanana-slide-deck' -type d 2>/dev/null | head -1)/references/styles"
# Source 1: baoyu-slide-deck styles (16 slide-optimized)
BAOYU_SLIDE=$(ls -td ~/.claude/plugins/cache/baoyu-skills/content-skills/*/skills/baoyu-slide-deck/references/styles 2>/dev/null | head -1)
# Source 2: baoyu-infographic styles (20 visual-rich)
BAOYU_INFOG=$(ls -td ~/.claude/plugins/cache/baoyu-skills/content-skills/*/skills/baoyu-infographic/references/styles 2>/dev/null | head -1)
# Source 3: theme-factory themes (10 color/typography presets)
THEME_FACTORY=$(ls -td ~/.claude/plugins/cache/anthropic-agent-skills/document-skills/*/skills/theme-factory/themes 2>/dev/null | head -1)
統合:4 つのディレクトリすべてで *.md を Glob し、ファイル名で重複を除外します(優先順位:baoyu-slide-deck > local > baoyu-infographic > theme-factory)。R3 で統合リスト全体をユーザーに提示します。
見つからない場合:PaperBanana の組み込みプリセット 23 種を使用します。
R2:コンテンツを分析する — トピック、対象読者、トーン、推奨スライド枚数。
R3:スタイルを提案する — コンテンツの特徴に基づいて 2~3 種のスタイルを提案します。スタイルとコンテンツの対応表全体は references/style-guide.md を参照してください。
R4:対話形式で選択する — AskUserQuestion を使用して、スタイル、対象読者、スライド枚数を確認します。
R5:分析結果を保存する — slide-deck/{topic-slug}/analysis.md を作成します。
Phase D:デザイン(新規モードのみ)
Modify モードでは、ユーザーがプロンプトを直接編集します。
D1:スタイル仕様を読み込む — baoyu のスタイル、または PaperBanana の組み込みスタイルから読み込みます。
D2:アウトラインを生成する — スライド構成を記載した outline.md を作成します。references/outline-format.md を参照してください。
D3:任意のアウトラインレビュー — 続行前にレビューするかどうかをユーザーに確認します。
D4:プロンプトを生成する — 各スライドについて、prompts/NN-slide-{slug}.md を作成します。各プロンプトファイルには、スタイル指示ブロック、続いて ---、その後にスライド固有の内容とデザイン要件を記述します。
Phase I:実装(両モード)
Step I1:画像生成(2 つの経路)
Path A — PaperBanana CLI(推奨、critic による品質レビューあり):
cd <project_root>
python -m paperbanana.cli slide-batch \
--prompts-dir "slide-deck/{topic-slug}/prompts" \
--output-dir "slide-deck/{topic-slug}" \
--resolution 4k \
--iterations 2
自動切り替えの条件: いずれかのスライド(最初のスライドだけではありません)で以下のいずれかが発生した場合、Path B に切り替えます。Path A の再試行を続けないでください。
RetryError/ResourceExhausted/ HTTP 429(レート制限)ServerError(5xx)または 429 以外のClientError(4xx)- いずれか 1 枚のスライドで Critic の処理に 3 分以上かかる
- ユーザーから「一直 429」/レート制限が続いているとの報告があった場合 → 直ちに切り替え、Path A の再試行をスキップします
2 種類の 429 の発生元を見分ける:
| 発生元 | 症状 | 対応 |
|---|---|---|
| Critic 429(VLM のレート制限) | Critic のループ内でエラーが発生。Path A 固有 | Path B に自動切り替え(Critic を使わないため、問題を回避できます) |
画像生成 429(gemini-3-pro-image-preview) | Path B でも同じエラーが発生 | 指数バックオフ:time.sleep(30 * 2**retry) を最大 5 分まで行い、その後ユーザーに通知します |
一部バッチの復旧: 429 が発生する前に生成済みのスライドがある場合は、既存の PNG を保持し、次に不足しているスライドから再開します(prompts/*.md と既存の NN-slide-*.png を照合します)。
注(上流との同期待ち): 上流の
paperbananaCLI(≥ #129)では、Path A のslide-batchにネイティブなbatch_checkpoint.jsonがあり、再開は自動で行われます。上記のスキップ処理は、上流のチェックポイント形式をまだ使用していない Path B のフォールバックコード向けです。
Path B — Gemini API を直接使用(フォールバック、Critic なし、安定):
from google import genai
from google.genai import types
from PIL import Image
from io import BytesIO
import base64, os, time
# Read API key from paperbanana/.env
client = genai.Client(api_key=GOOGLE_API_KEY)
config = types.GenerateContentConfig(
response_modalities=['IMAGE'],
image_config=types.ImageConfig(
aspect_ratio='16:9',
image_size='4K',
),
)
MAX_RETRIES = 5 # after which surface to user
BASE_SLEEP = 30 # seconds
for prompt_file in sorted(os.listdir(prompts_dir)):
name = prompt_file.replace('.md', '')
out_path = os.path.join(output_dir, f'{name}.png')
# Partial-batch resume: skip slides already generated
if os.path.exists(out_path):
continue
with open(os.path.join(prompts_dir, prompt_file), 'r', encoding='utf-8') as f:
prompt_text = f.read()
# Exponential backoff on 429 / transient errors
for retry in range(MAX_RETRIES):
try:
response = client.models.generate_content(
model='gemini-3-pro-image-preview',
contents=prompt_text,
config=config,
)
break
except Exception as e:
msg = str(e).lower()
if '429' in msg or 'resource' in msg or 'rate' in msg:
wait = min(BASE_SLEEP * (2 ** retry), 300) # cap at 5 min
time.sleep(wait)
continue
raise # non-429 error → surface immediately
else:
raise RuntimeError(f'{name}: 429 after {MAX_RETRIES} retries, surface to user')
for part in response.candidates[0].content.parts:
inline = getattr(part, 'inline_data', None)
if inline and getattr(inline, 'data', None):
data = inline.data
img_bytes = base64.b64decode(data) if isinstance(data, str) else data
img = Image.open(BytesIO(img_bytes))
img.save(out_path, 'PNG')
break
time.sleep(BASE_SLEEP) # baseline cooldown between slides
API キーは <project_root>/paperbanana/.env に GOOGLE_API_KEY として保存されています。dotenv を使用するか、手動で読み取ってください。
Step I2:スマートクリーンアップ(マージ前)
I1 の生成後に実行し、前には実行しないでください。 I1 は一部バッチの再開に対応しており、生成済みのスライドをスキップします。クリーンアップの役割は、前回の実行後にプロンプトが削除または名前変更された PNG のみを削除することです。I1 の前にクリーンアップを実行すると、再開するバッチの有効な WIP スライドが削除されます。
実行順序:I1 (generate + resume) → I2 (cleanup orphans) → I3 (merge)。
prompts/*.mdをスキャン → 期待される PNG のセットを作成します(例:01-slide-hook.md→01-slide-hook.png)- 対象ディレクトリをスキャンし、パターン
NN-slide-*.pngに一致する*.pngファイルを探します - 期待されるセットに含まれないスライド PNG を削除します(名前変更または削除されたプロンプトに由来する孤立ファイル)
- スライド以外の PNG(例:
logo.png、background.png)と、期待されるセット内のすべての PNG は保持します
Step I3:PPTX にマージする
# Locate skill directory dynamically (works on any platform)
SKILL_DIR="$(find ~/.claude/skills .claude/skills -maxdepth 1 -name 'paperbanana-slide-deck' -type d 2>/dev/null | head -1)"
bun "${SKILL_DIR}/scripts/merge-to-pptx.ts" "slide-deck/{topic-slug}"
マージスクリプトを利用できない場合:PNG 画像は用意できていますが、PPTX へのマージには bun とローカルの merge-to-pptx.ts スクリプトが必要であることをユーザーに伝えます。
フェーズ V:検証(両方のモード)
V1:結果の表示 — 生成された各スライド画像を読み込み、表示します。スライドの合計数、出力ディレクトリ、PPTX ファイルのパスを報告します。
V2:ユーザーからのフィードバック — 次のいずれかを尋ねます。
- 「いい感じです。完了です!」
- 「再生成してください。プロンプトで修正する内容を伝えます」
V3:再生成 — ユーザーが変更を希望する場合:
- ユーザーが該当するプロンプトファイルを編集します
- 変更したものだけでなく、すべてのスライドを再生成します(ビジュアルの一貫性を保つため)
- スマートクリーンアップを実行し、PPTX を再結合します
- V1 に戻ります
フォールバックの動作
| 状況 | フォールバック |
|---|---|
| スタイルライブラリがすべて見つからない | PaperBanana の組み込みプリセットを使用 |
| ローカルの結合スクリプトが見つからない | PNG のみを出力し、PPTX はスキップ |
| Bun/npx が見つからない | PPTX の結合をスキップし、ユーザーに知らせる |
| Critic 429 / RetryError | 自動的に Path B(critic なし)に切り替える |
| Path B で画像生成が 429 になった場合 | 最大 5 分まで指数バックオフし、その後ユーザーに知らせる |
| バッチの一部がすでに生成されている | 次に不足しているスライドから再開し、既存の PNG を保持する |
出力構造
slide-deck/{topic-slug}/
analysis.md ← R phase (New mode only)
outline.md ← D phase
prompts/ ← D phase
01-slide-hook.md
02-slide-cover.md
...
01-slide-hook.png ← I phase
02-slide-cover.png
...
{topic-slug}.pptx ← I phase
クイックリファレンス
# Path A: PaperBanana CLI batch
python -m paperbanana.cli slide-batch --prompts-dir prompts/ --resolution 4k --iterations 2
# Path A: single slide (supports --vlm-model)
python -m paperbanana.cli slide --input prompt.md --resolution 4k
# List available styles
python -m paperbanana.cli slide --list-styles