agentleFS
jackchukajackchuka/skills/p-md-to-slides/SKILL.md

p-md-to-slides

Skill15 starsChanged 9 days agoMITView on GitHub

Convert a structured Markdown deck source into a styled Google Slides presentation. Use this skill when the user has a slide.md file (or wants to write one) and wants Google Slides output — talks, LT decks, lecture slides, conference presentations, internal explainers. Trigger phrases include "md からスライド作って", "slide.md を Google Slides にして", "発表資料を Google Slides で", "convert this markdown to slides", "build a Google Slides deck from this markdown", "make slides from md", "/md-to-slides", and similar. Also use when the user references a presentation/<date>/slide.md file structure with `## Slide N — title`, `**話すこと:**`, and `**スライド要素:**` sections.

The file

We split this skill into the rules it gives an agent

6 rules. About 0 are common, 1 is distinctive, and 5 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 5

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Not for activity-driven decks (see o-tailor-allhands-deck for those) — this is for hand-authored Markdown → Google Slides.

    line 25
  2. Very specific:

    Use the Read tool on the user's slide.md.

    line 58
  3. Very specific:

    If the user is starting from scratch, offer to scaffold a slide.md from a brief.

    line 63
  4. Very specific:

    Read the relevant theme reference file based on user preference (default tailor if not specified):

    line 67
  5. Distinctive:

    If the user wants something custom, ask for brand colors + display font and adapt inline.

    line 73
  6. Very specific:

    Write a one-off /tmp/p-md-to-slides/<deck-name>/build.py that emits batchUpdate JSON.

    line 142

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

p-md-to-slides

Convert a Markdown deck source into a styled Google Slides presentation, with a chosen design theme and speaker notes pushed from the source.

When to use

  • The user has a slide.md (or similar) with ## Slide N — title sections and wants Google Slides
  • The user is preparing a talk / LT / conference presentation and has drafted the content in Markdown
  • The user asks to convert / build / generate slides from a Markdown source

Not for activity-driven decks (see o-tailor-allhands-deck for those) — this is for hand-authored Markdown → Google Slides.

Prerequisites

  • gws CLI installed and authenticated (gws auth login if needed)
  • Python 3 on PATH (for one-shot heredoc snippets)
  • Network access to Google Slides API

Input format

See references/md-format.md for the full spec. Quick summary:

# <deck title>

## Slide N — <subtitle>

**話すこと:**
<speaker notes — pushed to the slide's notes BODY>

**スライド要素:**
- <visual element hint 1>
- <visual element hint 2>

Both **話すこと:** and **スライド要素:** are optional per slide.

Workflow

The skill is intentionally lightweight — no shipped scripts. Every step is a concrete shell or Python heredoc that Claude executes inline, with patterns referenced from references/.

Step 1: Read the source

Use the Read tool on the user's slide.md. Confirm:

  • The file exists and parses (look for at least one ## Slide N — line)
  • The slide count matches what the user expects
  • Identify which slides have **話すこと:** and which don't

If the user is starting from scratch, offer to scaffold a slide.md from a brief.

Step 2: Pick a theme

Read the relevant theme reference file based on user preference (default tailor if not specified):

  • references/themes/tailor.md — Tailor brand (navy/cream/blue, DM Sans + Roboto Mono, alternating rhythm)
  • references/themes/minimal.md — All-cream, navy text
  • references/themes/dark.md — All-navy, cream text, electric blue accents

If the user wants something custom, ask for brand colors + display font and adapt inline.

Step 3: Parse the markdown

A short Python heredoc handles the parsing. The format is small enough that this beats a separate script.

python3 - <<'PY' > /tmp/p-md-to-slides/parsed.json
import json, re, sys
from pathlib import Path

SRC = "/path/to/slide.md"  # ← fill in
SLIDE_HEADER = re.compile(r"^##\s*Slide\s*(\d+)\s*[—–-]\s*(.+?)\s*$")
BLOCK_MARKER = re.compile(r"^\*\*([^*]+):\*\*\s*$")
BULLET = re.compile(r"^\s*[-*]\s+(.*)$")

text = Path(SRC).read_text(encoding="utf-8")
lines = text.splitlines()

title = next((l[2:].strip() for l in lines if l.startswith("# ") and not l.startswith("## ")), "")
starts = [i for i, l in enumerate(lines) if SLIDE_HEADER.match(l)]
slides = []
for idx, start in enumerate(starts):
    end = starts[idx+1] if idx+1 < len(starts) else len(lines)
    m = SLIDE_HEADER.match(lines[start])
    blocks = {}
    code_blocks = []
    current = None
    in_code = False
    code_buf = []
    for line in lines[start+1:end]:
        if line.lstrip().startswith("```"):
            if in_code:
                code_blocks.append("\n".join(code_buf)); code_buf = []; in_code = False
            else:
                current = None; in_code = True
            continue
        if in_code: code_buf.append(line); continue
        mm = BLOCK_MARKER.match(line)
        if mm:
            current = mm.group(1).strip()
            blocks.setdefault(current, []); continue
        if line.strip() == "---":
            current = None; continue
        if current is not None:
            blocks[current].append(line)
    notes = "\n".join(blocks.get("話すこと", [])).strip()
    elements_raw = "\n".join(blocks.get("スライド要素", [])).strip()
    elements = [BULLET.match(l).group(1).strip() for l in elements_raw.splitlines() if BULLET.match(l)]
    slides.append({"index": idx, "number": int(m.group(1)), "subtitle": m.group(2).strip(),
                    "notes": notes, "elements": elements, "elements_raw": elements_raw,
                    "code_blocks": code_blocks})
print(json.dumps({"title": title, "slides": slides}, ensure_ascii=False, indent=2))
PY

Inspect the JSON briefly (slide count, notes lengths) before continuing.

Step 4: Create the presentation shell

gws slides presentations create --json '{"title":"<deck title>"}' \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print('PRES_ID=' + d['presentationId']); print('BLANK=' + next(l['objectId'] for l in d.get('layouts',[]) if l.get('layoutProperties',{}).get('displayName')=='Blank')); print('DEFAULT=' + d['slides'][0]['objectId'])"

Capture PRES_ID, BLANK (Blank layout ID), and DEFAULT (default slide ID — must delete later).

Step 5: Build the deck

Write a one-off /tmp/p-md-to-slides/<deck-name>/build.py that emits batchUpdate JSON. Read references/slides-helpers.md for the helper code pattern (page constants, color blending, Slide builder class) — copy the helpers into your build script, then add per-slide content.

The base recipe per slide:

  1. create_slide(slide_id, index, BLANK, bg=theme_bg_for_index) — bg cycles per theme rhythm
  2. top_bar(...) — small uppercase labels (e.g. "05 · 工夫① ...")
  3. Heading — 22–24pt w700 for content slides, 40pt w600 bottom-anchored for the title slide
  4. Body — 16–18pt w600–700 bullets from elements, or fall back to a clean prose dump

Projection-readable defaults. Sizes are calibrated for talks projected on a 3–4m screen viewed from 5–10m back. A deck that "looks balanced" in your laptop preview is usually too small in the room. Default heavy and big; trim only when content actually overflows. See the typography table in the chosen theme file for the full role-by-role scale.

At the end, delete(DEFAULT) to remove the auto-created blank slide, then push the batch:

BODY=$(cat /tmp/p-md-to-slides/<deck-name>/batch.json)
gws slides presentations batchUpdate \
  --params '{"presentationId":"<PRES_ID>"}' \
  --json "$BODY"

batchUpdate is transactional — if anything fails, nothing applies. Inspect the error response carefully.

Step 6: Push speaker notes

Discover the notes BODY shape per slide, then insertText:

gws slides presentations get \
  --params '{"presentationId":"<PRES_ID>","fields":"slides(objectId,slideProperties.notesPage.pageElements(objectId,shape.placeholder))"}' \
  | python3 -c "
import json, sys
d = json.load(sys.stdin)
for s in d['slides']:
    sid = s['objectId']
    for el in s.get('slideProperties', {}).get('notesPage', {}).get('pageElements', []):
        if el.get('shape', {}).get('placeholder', {}).get('type') == 'BODY':
            print(f'{sid}|{el[\"objectId\"]}')
"

For each slide, build an insertText request against the notes BODY objectId with the slide's notes text. Skip slides without notes. Don't use deleteText on empty notes — see gotcha in references/slides-api-patterns.md.

Step 7: Verify with thumbnails

mkdir -p /tmp/p-md-to-slides/<deck-name>/thumbs
gws slides presentations get --params '{"presentationId":"<PRES_ID>","fields":"slides.objectId"}' \
  | python3 -c "import json,sys; [print(s['objectId']) for s in json.load(sys.stdin)['slides']]" \
  | nl | while read i sid; do
    url=$(gws slides presentations pages getThumbnail \
      --params "{\"presentationId\":\"<PRES_ID>\",\"pageObjectId\":\"$sid\",\"thumbnailProperties.mimeType\":\"PNG\",\"thumbnailProperties.thumbnailSize\":\"LARGE\"}" \
      | python3 -c "import json,sys;print(json.load(sys.stdin)['contentUrl'])")
    curl -s -o "/tmp/p-md-to-slides/<deck-name>/thumbs/$(printf %02d $i)_${sid}.png" "$url"
  done

Then Read each PNG to inspect. The slide is only 405pt tall — content past y≈395 is invisible. The thumbnail is the only catch.

Step 8: Iterate on visual polish

The base deck has heading + body only. Editorial polish is content-aware and applied as a per-deck upgrade.py. Common moves (all using the same Slide builder pattern from references/slides-helpers.md):

  • Decorative anchor character (top-right): a single oversized Roboto Mono glyph in dim color. Numbers ("01" / "02" / "03") for ordered section slides, but also "?" on question slides, "/" on transitions, "※" on caveats. The anchor labels the slide without adding reading load.
  • Subhead (tagline) under heading: 12–14pt muted single line at y≈95. Names the slide's thesis — the "why am I looking at this?" — not just the topic. Particularly effective on section / content slides where the heading alone is the topic name.
  • "Before / After" or "Journey" slides: colored rectangles with arrow between, accent badge for the "why"
  • Flow diagrams: colored boxes per stage, contrasting fills for branches
  • Title slide: small accent bar (theme accent) above the headline
  • Summary slides (simple): 2×2 cards with light fill and left accent bar
  • Summary slides (story-structured): narrative-numbered cards (01 課題 / 02 解決 / 03 品質 / 04 未来). Use when the recap has a sequence, not just 4 independent points — the numbers tell the audience this is a story arc.
  • Dark footer band (cream summary closer): navy band at the bottom of a cream summary slide holding CTA / contact info. Acts as a visual period — physically separates "what we covered" from "thank you / find me here."

Apply by writing a fresh batchUpdate, re-fetch thumbnails, iterate.

Step 9: Deliver

https://docs.google.com/presentation/d/<PRES_ID>/edit

Key gotchas (full list: references/slides-api-patterns.md)

  • Page height is 405pt — not 540. Anything past y≈395 is off-canvas.
  • Object IDs must be unique across the entire presentation. Don't reuse.
  • Empty notes: startIndex==endIndex==0; use insertText only, never deleteText.
  • batchUpdate is transactional: one failure rolls back the whole batch.
  • z-order is creation order: backgrounds must be SEND_TO_BACK explicitly.
  • DM Sans + Japanese: falls back, may render slightly oblique. Acceptable; verify with thumbnails.

Reference files

  • references/md-format.md — input format spec
  • references/slides-api-patterns.md — gotchas, color math, request shapes
  • references/slides-helpers.md — reusable Python helper class (copy into build.py)
  • references/themes/{tailor,minimal,dark}.md — design tokens per theme

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.