Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@
"url": "https://nothingfancy.ai"
},
"source": "./plugins/launching-google-ai"
},
{
"name": "cheatsheet-pdf",
"version": "0.1.0",
"description": "Renders a Markdown document into a compact, print-ready PDF cheatsheet — a dense single-page (or single-sheet, two-sided) reference — using pandoc and headless Chromium, auto-shrinking the layout to fit a target page count. Use when asked to make a cheatsheet, turn a Markdown/README/notes file into a printable one-page PDF, build a quick-reference or pin-up sheet, or \"fit this on one page\". Not for long-form documents, slide decks, or faithfully reproducing a source document's existing formatting.",
"author": {
"name": "Wesley O. Nichols",
"email": "wes@nothingfancy.ai",
"url": "https://nothingfancy.ai"
},
"source": "./plugins/cheatsheet-pdf"
}
]
}
1 change: 1 addition & 0 deletions .codex/skills/cheatsheet-pdf
1 change: 1 addition & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/plugins/nano-banana-prompting/ @wesnick
/plugins/gemini-tts-prompting/ @wesnick
/plugins/launching-google-ai/ @wesnick
/plugins/cheatsheet-pdf/ @wesnick

# Shared infrastructure
/.github/ @wesnick
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ This is the preferred mode when you want a narrow, focused session.
| [gemini-tts-prompting](plugins/gemini-tts-prompting/) | Crafts and reviews prompts for Google's Gemini 3.1 Flash TTS using audio tags, voice style instructions, and pacing controls — narration, audiobooks, IVR, accessibility, multilingual content. |
| [launching-google-ai](plugins/launching-google-ai/) | Opens Google Gemini or Google Stitch in the browser with a pre-filled prompt and optional Gemini tool selection (image, video, music, deep research, canvas, guided learning) via URL parameters. |

### Documents

| Plugin | Description |
|--------|-------------|
| [cheatsheet-pdf](plugins/cheatsheet-pdf/) | Renders a Markdown document into a compact, print-ready PDF cheatsheet (dense single-page or single-sheet two-sided) via pandoc and headless Chromium, auto-shrinking the layout to fit a target page count. |

## Philosophy

Read [`AGENTS.md`](AGENTS.md) before authoring. The short version:
Expand Down
83 changes: 83 additions & 0 deletions evals/cases/cheatsheet-pdf/cases.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Regression suite for cheatsheet-pdf skill

description: "cheatsheet-pdf skill regression cases"

tests:
# --- Positive: the core ask ---
- description: "Positive — turn a Markdown reference into a one-page PDF"
vars:
prompt: |
I have a KEYBINDINGS.md file with a bunch of tables and short
sections. Can you make it into a single-page printable PDF cheatsheet
and open it for me?
assert:
- type: contains-any
value:
- "build_cheatsheet.py"
- "pandoc"
- type: contains-any
value:
- "--open"
- "--max-pages"
- type: llm-rubric
value: |
The response should use the cheatsheet-pdf skill: run the bundled
build_cheatsheet.py against KEYBINDINGS.md to produce a one-page
PDF (max-pages 1, the default) and open it. It should rely on the
script's auto-fit rather than hand-tuning font sizes, and ideally
mention verifying the rendered output, not just the page count. It
should confirm pandoc + a Chromium browser are available.

# --- Positive: two-sided + verify discipline ---
- description: "Positive — single sheet two-sided, content too dense for one side"
vars:
prompt: |
This API_REFERENCE.md is pretty long. Make it a printable cheatsheet —
I'm fine with a single sheet printed on both sides if it won't fit on
one side legibly.
assert:
- type: contains
value: "--max-pages 2"
- type: llm-rubric
value: |
The response should pass --max-pages 2 (single sheet, two-sided),
and should treat legibility as the deciding factor: if one side
would require illegibly small text, prefer two-sided rather than
shrinking into a wall of tiny text. Bonus if it inspects the
rendered PDF to confirm legibility.

# --- Negative: long-form document, not a cheatsheet ---
- description: "Negative — long-form report should not become a cheatsheet"
vars:
prompt: |
I wrote a 12-page quarterly report in REPORT.md. Please convert it to
a nicely formatted PDF I can email to the board.
assert:
- type: not-contains
value: "--max-pages 1"
- type: llm-rubric
value: |
The response should NOT force this into a dense one-page cheatsheet.
A 12-page board report is long-form reading, where flow matters more
than density. The skill should decline the cheatsheet treatment and
recommend a normal Markdown-to-PDF path (e.g. pandoc with a readable
single-column theme) instead.

# --- Edge: overlapping text in output (the table-overflow trap) ---
- description: "Edge — diagnose overlapping text in a rendered cheatsheet"
vars:
prompt: |
I built a cheatsheet PDF from my Markdown but it looks broken — in the
right-hand column the text from a big wide table is printed on top of
the other column's text. What's going on and how do I fix it?
assert:
- type: contains
value: "table-layout: fixed"
- type: llm-rubric
value: |
The response should diagnose this as horizontal overflow: a wide
table sized itself larger than its column and painted over the
neighbouring column. The fix is `table-layout: fixed` on tables
(which the bundled stylesheet already sets), forcing cells to wrap
within the column. It should NOT misattribute the overlap to a
page-break or vertical-spacing problem.
10 changes: 10 additions & 0 deletions plugins/cheatsheet-pdf/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "cheatsheet-pdf",
"version": "0.1.0",
"description": "Renders a Markdown document into a compact, print-ready PDF cheatsheet — a dense single-page (or single-sheet, two-sided) reference — using pandoc and headless Chromium, auto-shrinking the layout to fit a target page count. Use when asked to make a cheatsheet, turn a Markdown/README/notes file into a printable one-page PDF, build a quick-reference or pin-up sheet, or \"fit this on one page\". Not for long-form documents, slide decks, or faithfully reproducing a source document's existing formatting.",
"author": {
"name": "Wesley O. Nichols",
"email": "wes@nothingfancy.ai",
"url": "https://nothingfancy.ai"
}
}
78 changes: 78 additions & 0 deletions plugins/cheatsheet-pdf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# cheatsheet-pdf

Renders a Markdown document into a compact, print-ready PDF cheatsheet.

## What it does

Turns a Markdown reference into a dense, good-looking PDF that fits on a single printable side — or one sheet, two-sided. The pipeline is Markdown → HTML → PDF:

- **`pandoc`** converts the Markdown (tables, code, links) to HTML.
- **A print-tuned stylesheet** (`assets/cheatsheet.css`) supplies the cheatsheet look: balanced multi-column flow, color-coded section bars, boxed code, callouts.
- **Headless Chromium** renders the HTML to PDF, doing the column layout and page breaking.

The headline feature is **auto-fit**: rather than hand-tuning font sizes until the content fits, the build script binary-searches the largest uniform zoom whose render still respects the page budget (`--max-pages`, default 1). The sheet ends up as full as it can be without spilling over.

Two layout details are load-bearing and documented in the skill: tables use `table-layout: fixed` (otherwise a wide table overflows and paints over its neighbour), and the title renders outside the column flow (a column-spanning title corrupts downstream positions).

## Requirements

External tools on PATH (not npm/pip packages):

- `pandoc`
- a Chromium browser — `google-chrome`, `chromium`, `chromium-browser`, `microsoft-edge`, or `brave-browser`

The build script is stdlib-only Python (`uv run` or `python3`).

## Usage

### Build a cheatsheet

```sh
uv run plugins/cheatsheet-pdf/skills/cheatsheet-pdf/scripts/build_cheatsheet.py NOTES.md --open
```

```sh
# single sheet, two-sided
uv run .../build_cheatsheet.py NOTES.md --max-pages 2
# three columns, A4
uv run .../build_cheatsheet.py NOTES.md --columns 3 --page-size a4
# fixed density, no search
uv run .../build_cheatsheet.py NOTES.md --no-fit --scale 0.9
```

### Claude Code

```sh
claude --plugin-dir ./plugins/cheatsheet-pdf
```

### pi

```sh
pi -e ./plugins/cheatsheet-pdf
```

### Codex

See the root [`.codex/INSTALL.md`](../../.codex/INSTALL.md).

## Harness support

| Feature | Claude Code | Codex | pi |
|---|---|---|---|
| Skill (SKILL.md) | ✅ | ✅ | ✅ |
| Bundled script + CSS | ✅ | ✅ | ✅ |

Pure skill content plus a harness-neutral script — runs identically in all three. The script shells out to `pandoc` and a Chromium browser; it names no harness-specific tools.

## Evals

Regression cases live in [`../../evals/cases/cheatsheet-pdf/cases.yaml`](../../evals/cases/cheatsheet-pdf/cases.yaml).

```sh
uv run evals/framework/run.py --skill cheatsheet-pdf
```

## License

[CC BY-SA 4.0](../../LICENSE) — made by [Nothing Fancy](https://nothingfancy.ai).
18 changes: 18 additions & 0 deletions plugins/cheatsheet-pdf/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "@nothingfancy/cheatsheet-pdf",
"version": "0.1.0",
"description": "Renders a Markdown document into a compact, print-ready PDF cheatsheet.",
"keywords": ["pi-package", "agent-skill", "cheatsheet", "pdf", "pandoc", "markdown"],
"author": {
"name": "Wesley O. Nichols",
"email": "wes@nothingfancy.ai",
"url": "https://nothingfancy.ai"
},
"license": "CC-BY-SA-4.0",
"pi": {
"skills": ["./skills"]
},
"peerDependencies": {
"@mariozechner/pi-coding-agent": "*"
}
}
86 changes: 86 additions & 0 deletions plugins/cheatsheet-pdf/skills/cheatsheet-pdf/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
name: cheatsheet-pdf
description: Renders a Markdown document into a compact, print-ready PDF cheatsheet — a dense single-page (or single-sheet, two-sided) reference — using pandoc and headless Chromium, auto-shrinking the layout to fit a target page count. Use when asked to make a cheatsheet, turn a Markdown/README/notes file into a printable one-page PDF, build a quick-reference or pin-up sheet, or "fit this on one page". Not for long-form documents, slide decks, or faithfully reproducing a source document's existing formatting.
---

# cheatsheet-pdf

Turns a Markdown reference into a dense, good-looking PDF that fits on a single printable side (or one sheet, two-sided). The pipeline is Markdown → HTML → PDF: `pandoc` does the content conversion, a print-tuned stylesheet does the look, and headless Chromium does the layout and rendering.

The guiding principle: **let the renderer do the layout, and let a search find the density.** Don't hand-tune font sizes until it fits — the bundled script does that automatically by finding the largest uniform zoom that still respects the page budget.

## When to Use

- The user asks to "make a cheatsheet", "turn this into a one-pager", "fit this on a single page", or "make a printable reference" from a Markdown file (a README, notes, a command list, an API summary).
- The user wants a pin-up / quick-reference sheet: dense, scannable, multi-column, color-coded sections.
- The user has a Markdown doc with tables, code blocks, and short sections that should collapse onto one sheet.
- The user wants a single-sheet two-sided handout (`--max-pages 2`).

## When NOT to Use

- **Long-form documents** (reports, articles, manuals) where reading flow matters more than density — use a normal Markdown-to-PDF path (`pandoc -o out.pdf`) with a readable single-column theme, not this.
- **Slide decks / presentations** — use Marp, reveal.js, or Slidev.
- **Faithful reproduction** of an existing document's formatting (a styled spec, a branded template) — this re-flows content into a cheatsheet layout and will not preserve the original look.
- **No renderer available** — this needs both `pandoc` and a Chromium browser on PATH. If neither is installable, fall back to `pandoc`'s own PDF engines and say so.
- The content genuinely doesn't fit one side and shrinking would make it illegible — say so and recommend `--max-pages 2` rather than producing a 4pt wall of text.

## The tools

Two bundled files do the work:

- `scripts/build_cheatsheet.py` — the build script (stdlib-only, run with `uv run`).
- `assets/cheatsheet.css` — the default print stylesheet (the cheatsheet look).

Both `pandoc` and a Chromium browser (`google-chrome`, `chromium`, …) must be on PATH. Confirm before promising output.

## Quick start

```bash
uv run scripts/build_cheatsheet.py NOTES.md --open
```

That auto-fits onto **one** Letter page and opens it in the default viewer. The script prints the page count and the zoom it settled on.

Common variations:

```bash
# Single sheet, two-sided:
uv run scripts/build_cheatsheet.py NOTES.md --max-pages 2

# Three columns / A4:
uv run scripts/build_cheatsheet.py NOTES.md --columns 3 --page-size a4

# Pin the density yourself (skip the search):
uv run scripts/build_cheatsheet.py NOTES.md --no-fit --scale 0.9

# Custom stylesheet:
uv run scripts/build_cheatsheet.py NOTES.md --css my-theme.css
```

## How it works (and why)

**1. The title is pulled out of the column flow.** A leading `# Title` line is rendered as a full-width banner *above* the multi-column container. Making the title a column-spanning element inside the flow instead causes the renderer to miscalculate positions for everything after it. Keep the title outside the columns.

**2. Content flows into balanced columns.** Multi-column layout is the whole trick: it packs dense reference material into far less vertical space than a single column, which is what lets a long doc collapse onto one side. Two columns is the default; three suits very short line lengths.

**3. Tables are constrained, not free.** The stylesheet sets `table-layout: fixed` on every table. This is load-bearing, not cosmetic: a wide table left to size itself will grow past its column and **paint on top of the neighbouring column** — text overlapping text. Fixed layout forces cells to wrap within the column. If you ever see overlapping text in the output, an unconstrained-width element is the cause.

**4. Auto-fit finds the density.** With a page budget (`--max-pages`, default 1), the script binary-searches the largest whole-sheet zoom whose render still fits the budget. Fewer pages is monotonic in zoom, so the search is well-defined. The result fills the page as much as possible without spilling — the same outcome as hand-tuning font sizes, found in ~7 renders. `--no-fit --scale X` bypasses it.

## Verify the output

Page count is necessary but not sufficient — **look at the rendered PDF**, don't just trust the page number. Open it (`--open`) or read it back. Check specifically for:

- **Overlapping text** → a width-unconstrained element (almost always a table missing `table-layout: fixed`, or a very long unbroken token).
- **Illegibly small text** → the budget is too tight; recommend `--max-pages 2` or fewer columns instead of squinting.
- **A nearly-empty trailing page** → the budget allows slack; usually fine, but a smaller `--max-pages` or more content may pack better.

## Customizing the look

Edit `assets/cheatsheet.css` (or pass `--css` with a copy). Safe to change: fonts, colors, the section-header bars, spacing, borders. Leave three knobs to the script — it appends overrides for them and will fight manual edits: `@page { size }`, `.cols { column-count }`, and `html { zoom }`.

## Known edges

- **Emoji** render in color only if a color-emoji font (e.g. Noto Color Emoji) is installed; otherwise they fall back to monochrome or boxes.
- **Very long unbreakable tokens** (a 90-char URL with no separators) can still force a wide column. `overflow-wrap: anywhere` handles most; pathological cases may need a manual `<wbr>` or a shortened link in the source.
- **`zoom`** is a Chromium feature; the rendering path assumes a Chromium-family browser, not Firefox or WebKit.
Loading
Loading