From 286c435c3007d73b5d68fe569918479e4fced740 Mon Sep 17 00:00:00 2001 From: Nana Dev Date: Fri, 7 Aug 2026 10:44:48 +0700 Subject: [PATCH] Add reproducible README demo --- .github/workflows/python-ci.yml | 4 +- CHANGELOG.md | 1 + MANIFEST.in | 3 +- README.md | 21 +++ docs/assets/creator-toolkit-demo.gif | Bin 0 -> 44507 bytes docs/demo.md | 37 +++++ pyproject.toml | 3 + scripts/render_readme_demo.py | 195 +++++++++++++++++++++++++++ 8 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 docs/assets/creator-toolkit-demo.gif create mode 100644 docs/demo.md create mode 100644 scripts/render_readme_demo.py diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index aac9852..2288f46 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -23,9 +23,11 @@ jobs: python-version: "3.13" cache: pip - name: Install project - run: python -m pip install --upgrade pip && python -m pip install -e ".[dev]" + run: python -m pip install --upgrade pip && python -m pip install -e ".[dev,demo]" - name: Lint and format run: ruff check . && ruff format --check . + - name: Rebuild README demo + run: python scripts/render_readme_demo.py --output .test-tmp-ci-demo/creator-toolkit-demo.gif - name: Audit dependencies run: python -m pip_audit - name: Build and inspect distributions diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e0cfa5..e9f505f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project are documented in this file. The format is b - Safe rename previews, confirmation, recovery manifests, and an `undo` command. - Machine-readable JSON output for every scriptable subcommand. - Installed CLI smoke coverage on Windows, macOS, and Linux. +- Reproducible animated README demo with real title, tag, and rename-preview workflows. - Python 3.11–3.13 test matrix, linting, coverage, and distribution checks. ### Changed diff --git a/MANIFEST.in b/MANIFEST.in index fc2bedf..9a29abd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,2 @@ -recursive-include docs *.md +include scripts/render_readme_demo.py +recursive-include docs *.gif *.md diff --git a/README.md b/README.md index c90f469..ac30425 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,27 @@ A lightweight Python CLI for repeatable creator workflows. Generate content ideas and tags, or normalize a folder of image names from an interactive menu or scriptable subcommands. +![Animated terminal demo showing title generation, tag selection, and a dry-run image rename preview](docs/assets/creator-toolkit-demo.gif) + +The demo uses disposable sample files. Title and tag suggestions vary during normal use. + +## Why Creator Toolkit? + +- Preview filename changes before touching a directory, then undo applied renames from a manifest. +- Automate every scriptable workflow with stable JSON schemas and meaningful exit codes. +- Get the same CLI behavior on current Windows, macOS, and Linux systems. +- Keep small creator tasks local, inspectable, and easy to compose in scripts. + +## Real workflows + +| Goal | Before | Example result | +| --- | --- | --- | +| Draft a title | `creator workflow` | `10 Creator Workflow Tips Every Beginner Should Know` | +| Pick three tags | `--count 3` | `automation`, `productivity`, `tutorial` | +| Preview image renames | `cover.jpg`, `thumbnail.png` | `image_1.jpg`, `image_2.png` — no files changed | + +See the [demo transcript and reproduction steps](docs/demo.md). + ## Features - Generate title ideas from a keyword. diff --git a/docs/assets/creator-toolkit-demo.gif b/docs/assets/creator-toolkit-demo.gif new file mode 100644 index 0000000000000000000000000000000000000000..fe239ad71a5ce59676de0c133882a58eea81d795 GIT binary patch literal 44507 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 literal 0 HcmV?d00001 diff --git a/docs/demo.md b/docs/demo.md new file mode 100644 index 0000000..fd38559 --- /dev/null +++ b/docs/demo.md @@ -0,0 +1,37 @@ +# README demo + +The animated README demo is generated from the current Creator Toolkit source. It calls the same +CLI entry logic shown to users, creates two non-sensitive sample image files in a temporary +directory, and removes that directory when rendering finishes. + +## Rebuild the asset + +From the repository root: + +```bash +python -m pip install -e ".[demo]" +python scripts/render_readme_demo.py +``` + +The renderer writes `docs/assets/creator-toolkit-demo.gif`. Title and tag examples use a fixed +seed in the renderer so reviews get a stable visual diff; normal CLI suggestions still vary +between runs. + +## Demo transcript + +```console +$ creator-toolkit title "creator workflow" +10 Creator Workflow Tips Every Beginner Should Know + +$ creator-toolkit tags --count 3 +automation +productivity +tutorial + +$ creator-toolkit rename demo-images --dry-run +Planned 2 image(s). +cover.jpg -> image_1.jpg +thumbnail.png -> image_2.png +``` + +The final command is a preview. It does not change either sample filename. diff --git a/pyproject.toml b/pyproject.toml index cfed180..96f3125 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,9 @@ classifiers = [ ] [project.optional-dependencies] +demo = [ + "Pillow>=12.0", +] dev = [ "build>=1.2.2", "pip-audit>=2.9", diff --git a/scripts/render_readme_demo.py b/scripts/render_readme_demo.py new file mode 100644 index 0000000..11af689 --- /dev/null +++ b/scripts/render_readme_demo.py @@ -0,0 +1,195 @@ +"""Render the animated README demo from real Creator Toolkit CLI output.""" + +from __future__ import annotations + +import argparse +import os +import subprocess +import sys +import tempfile +from pathlib import Path + +from PIL import Image, ImageDraw, ImageFont + +ROOT = Path(__file__).resolve().parents[1] +SOURCE = ROOT / "src" +DEFAULT_OUTPUT = ROOT / "docs" / "assets" / "creator-toolkit-demo.gif" +WIDTH = 900 +HEIGHT = 506 +SEED = 2026 + +BACKGROUND = "#0d1117" +PANEL = "#161b22" +BORDER = "#30363d" +TEXT = "#e6edf3" +MUTED = "#8b949e" +GREEN = "#3fb950" +BLUE = "#58a6ff" +PURPLE = "#a371f7" + +Line = tuple[str, str] + + +def _font(size: int, *, bold: bool = False) -> ImageFont.FreeTypeFont | ImageFont.ImageFont: + """Load a readable system monospace font on the supported platforms.""" + filename = "consolab.ttf" if bold else "consola.ttf" + candidates = [ + Path(os.environ.get("WINDIR", "C:/Windows")) / "Fonts" / filename, + Path( + "/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf" + if bold + else "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" + ), + Path("/System/Library/Fonts/Menlo.ttc"), + ] + for candidate in candidates: + if candidate.is_file(): + return ImageFont.truetype(str(candidate), size=size) + return ImageFont.load_default(size=size) + + +def _run_cli(arguments: list[str], *, cwd: Path) -> list[str]: + """Run the current source tree with a stable seed and return its output lines.""" + environment = os.environ.copy() + current_pythonpath = environment.get("PYTHONPATH") + environment["PYTHONPATH"] = os.pathsep.join( + value for value in (str(SOURCE), current_pythonpath) if value + ) + runner = ( + "import random, sys; " + f"random.seed({SEED}); " + "from creator_toolkit.main import main; " + "raise SystemExit(main(sys.argv[1:]))" + ) + result = subprocess.run( + [sys.executable, "-c", runner, *arguments], + cwd=cwd, + env=environment, + check=True, + capture_output=True, + text=True, + ) + return result.stdout.strip().splitlines() + + +def _capture_scenes() -> list[tuple[str, list[Line]]]: + """Capture title, tag, and safe-rename scenes from non-sensitive sample data.""" + with tempfile.TemporaryDirectory(prefix="creator-toolkit-demo-") as temporary_directory: + workspace = Path(temporary_directory) + images = workspace / "demo-images" + images.mkdir() + (images / "cover.jpg").write_bytes(b"demo") + (images / "thumbnail.png").write_bytes(b"demo") + + title = _run_cli(["title", "creator workflow"], cwd=workspace) + tags = _run_cli(["tags", "--count", "3"], cwd=workspace) + rename = _run_cli(["rename", "demo-images", "--dry-run"], cwd=workspace) + + return [ + ( + "TITLE IDEAS", + [ + ("prompt", '$ creator-toolkit title "creator workflow"'), + *(("output", line) for line in title), + ("blank", ""), + ("note", "Turn one keyword into a ready-to-refine headline."), + ], + ), + ( + "CURATED TAGS", + [ + ("prompt", "$ creator-toolkit tags --count 3"), + *(("output", line) for line in tags), + ("blank", ""), + ("note", "Unique suggestions, sorted for predictable output."), + ], + ), + ( + "SAFE RENAME PREVIEW", + [ + ("muted", "demo-images/"), + ("muted", " cover.jpg"), + ("muted", " thumbnail.png"), + ("blank", ""), + ("prompt", "$ creator-toolkit rename demo-images --dry-run"), + *(("output", line) for line in rename), + ("blank", ""), + ("success", "Preview only - no files changed."), + ], + ), + ] + + +def _draw_frame(label: str, lines: list[Line], step: int, total: int) -> Image.Image: + """Draw one terminal-style frame.""" + image = Image.new("RGB", (WIDTH, HEIGHT), BACKGROUND) + draw = ImageDraw.Draw(image) + body_font = _font(22) + body_bold = _font(22, bold=True) + small_font = _font(16) + + draw.rounded_rectangle((18, 18, WIDTH - 18, HEIGHT - 18), radius=14, fill=PANEL, outline=BORDER) + for x, color in ((42, "#ff5f56"), (66, "#ffbd2e"), (90, "#27c93f")): + draw.ellipse((x - 6, 38, x + 6, 50), fill=color) + draw.text((WIDTH // 2, 44), "creator-toolkit - demo", font=small_font, fill=MUTED, anchor="mm") + draw.line((19, 68, WIDTH - 19, 68), fill=BORDER, width=1) + + draw.rounded_rectangle((42, 86, 42 + 20 + len(label) * 10, 118), radius=16, fill="#21262d") + draw.text((54, 102), label, font=small_font, fill=PURPLE, anchor="lm") + + colors = { + "prompt": GREEN, + "output": TEXT, + "muted": MUTED, + "note": BLUE, + "success": GREEN, + "blank": TEXT, + } + y = 142 + for kind, line in lines: + font = body_bold if kind in {"prompt", "success"} else body_font + prefix = "[ok] " if kind == "success" else "" + draw.text((48, y), prefix + line, font=font, fill=colors[kind]) + y += 31 + + footer = "safe previews | JSON output | undo manifests" + draw.text((44, HEIGHT - 41), footer, font=small_font, fill=MUTED, anchor="lm") + draw.text( + (WIDTH - 44, HEIGHT - 41), f"{step}/{total}", font=small_font, fill=MUTED, anchor="rm" + ) + return image + + +def render(output: Path) -> None: + """Capture CLI output and write the optimized looping GIF.""" + scenes = _capture_scenes() + frames = [ + _draw_frame(label, lines, step=index, total=len(scenes)) + for index, (label, lines) in enumerate(scenes, start=1) + ] + palette_frames = [ + frame.quantize(colors=64, method=Image.Quantize.MEDIANCUT) for frame in frames + ] + output.parent.mkdir(parents=True, exist_ok=True) + palette_frames[0].save( + output, + save_all=True, + append_images=palette_frames[1:], + duration=[2200, 2200, 3200], + loop=0, + optimize=True, + disposal=2, + ) + + +def main() -> None: + """Parse arguments and render the README demo.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT) + arguments = parser.parse_args() + render(arguments.output.resolve()) + print(f"Rendered {arguments.output.resolve()}") + + +if __name__ == "__main__": + main()