diff --git a/mini_apps/README.md b/mini_apps/README.md
deleted file mode 100644
index 47c14c8..0000000
--- a/mini_apps/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Mini Apps
-
-This directory contains things that are too complex to be considered a "script", yet too personalized to be useful to anyone else.
-
-I may occasionally promote things from here to actual public repos someday.
\ No newline at end of file
diff --git a/mini_apps/memegen/bin/memegen b/mini_apps/memegen/bin/memegen
deleted file mode 100755
index 470d3d5..0000000
--- a/mini_apps/memegen/bin/memegen
+++ /dev/null
@@ -1,228 +0,0 @@
-#! /usr/bin/env python
-import argparse
-import sys
-import logging
-import json
-import subprocess
-from PIL import Image, ImageDraw, ImageFont
-from typing import List, Dict, Any, Optional
-from pathlib import Path
-from dataclasses import dataclass
-from enum import Enum
-from datetime import datetime
-
-logger = logging.getLogger(__name__)
-CONFIG_DIR = Path(__file__).parent.parent / "data"
-DEFAULT_OUTPUT_DIR = Path("~/Pictures/memes").expanduser()
-
-
-@dataclass
-class MemeTemplate:
-    image_path: Path
-    config: Dict[str, Any]
-
-
-class HorizontalAlignment(Enum):
-    LEFT = "left"
-    CENTER = "center"
-    RIGHT = "right"
-
-
-class VerticalAlignment(Enum):
-    TOP = "top"
-    CENTER = "center"
-    BOTTOM = "bottom"
-
-
-def discover_templates() -> List[str]:
-    # Find all directories in the templates directory
-    return [p.name for p in (CONFIG_DIR / "templates").glob("*") if p.is_dir()]
-
-
-def load_template(name: str) -> MemeTemplate:
-    logger.info(f"Loading template: {name}")
-
-    # Find the template directory
-    template_dir = CONFIG_DIR / "templates" / name
-    if not template_dir.exists():
-        logger.error(f"Template {name} does not exist")
-        sys.exit(1)
-
-    return MemeTemplate(
-        image_path=template_dir / "template.png",
-        config=json.loads((template_dir / "config.json").read_text()),
-    )
-
-
-def calc_width_from_image(width_str: str, image_width: int) -> int:
-    if width_str.endswith("%"):
-        return int(image_width * int(width_str[:-1]) / 100)
-    else:
-        return int(width_str)
-
-
-def render_text_on_image(image: Image, text: str, zone: str, config: Dict[str, Any]):
-    # NOTE: This must handle text with newlines
-    # Get the zone config
-    zone_config = config["zones"][zone]
-    horizontal_alignment = HorizontalAlignment(zone_config["horizontal_align"])
-    vertical_alignment = VerticalAlignment(zone_config["vertical_align"])
-    text_width = calc_width_from_image(zone_config["width"], image.width)
-    max_line_height = zone_config["max_line_height"]
-    font_path = CONFIG_DIR / "fonts" / config["font"]
-
-    # Create the font
-    font = None
-    font_size = 1
-    while True:
-        font = ImageFont.truetype(str(font_path), font_size)
-
-        # Split the text into lines
-        lines = text.splitlines()
-        bounding_boxes = []
-        for line in lines:
-            bounding_boxes.append(font.getbbox(line))
-
-        # Calculate the height of the text
-        line_height = max([bbox[3] for bbox in bounding_boxes])
-        total_height = sum(
-            [bbox[3] + zone_config["line_spacing"] for bbox in bounding_boxes]
-        )
-        max_width = max([bbox[2] for bbox in bounding_boxes])
-
-        # If we have a max line height, ensure we don't exceed it
-        if max_line_height and line_height > max_line_height:
-            font_size -= 1
-            break
-
-        # Don't exceed the width
-        if max_width > text_width:
-            font_size -= 1
-            break
-
-        # Increment the font size
-        font_size += 1
-
-    # Determine the starting Y position
-    y = zone_config["vertical_offset"]
-    if vertical_alignment == VerticalAlignment.CENTER:
-        y += (image.height - total_height) / 2
-    elif vertical_alignment == VerticalAlignment.BOTTOM:
-        y += image.height - total_height
-
-    # Render each line onto the image
-    draw = ImageDraw.Draw(image)
-    for line in text.splitlines():
-        # Calculate the x position
-        if horizontal_alignment == HorizontalAlignment.LEFT:
-            x = zone_config["horizontal_offset"]
-        elif horizontal_alignment == HorizontalAlignment.CENTER:
-            x = ((image.width - font.getbbox(line)[2]) / 2) + zone_config[
-                "horizontal_offset"
-            ]
-        elif horizontal_alignment == HorizontalAlignment.RIGHT:
-            x = (image.width - font.getbbox(line)[2]) + zone_config["horizontal_offset"]
-        else:
-            raise ValueError(f"Invalid horizontal alignment: {horizontal_alignment}")
-
-        # Render the text
-        draw.text(
-            (x, y),
-            line,
-            fill=tuple(config["fill_color"]),
-            stroke_fill=tuple(config["stroke_color"]),
-            stroke_width=config["stroke_width"],
-            font=font,
-        )
-
-        # Increment the y position
-        y += line_height + zone_config["line_spacing"]
-
-
-def main() -> int:
-    # Handle program arguments
-    ap = argparse.ArgumentParser(prog="memegen", description="Generates memes")
-    ap.add_argument(
-        "template", help="The template to use", choices=discover_templates()
-    )
-    ap.add_argument("--top-text", help="Top text (if applicable)")
-    ap.add_argument("--bottom-text", help="Bottom text (if applicable)")
-    ap.add_argument(
-        "--keep-case", help="Keep the case of the text", action="store_true"
-    )
-    ap.add_argument("--output", "-o", help="Output file path")
-    ap.add_argument(
-        "--no-show", help="Don't show the image after creation", action="store_true"
-    )
-    ap.add_argument(
-        "-v", "--verbose", help="Enable verbose logging", action="store_true"
-    )
-    args = ap.parse_args()
-
-    # Configure logging
-    logging.basicConfig(
-        level=logging.DEBUG if args.verbose else logging.INFO,
-        format="%(levelname)s:	%(message)s",
-    )
-
-    # Load the template
-    template = load_template(args.template)
-    template_supports_top_text = "top" in template.config["zones"]
-    template_supports_bottom_text = "bottom" in template.config["zones"]
-
-    # Ensure we have text
-    if args.top_text and not template_supports_top_text:
-        logger.error(f"Template {args.template} does not support top text")
-        sys.exit(1)
-    if args.bottom_text and not template_supports_bottom_text:
-        logger.error(f"Template {args.template} does not support bottom text")
-        sys.exit(1)
-    if not args.top_text and not args.bottom_text:
-        logger.error("No text provided")
-        if not all([template_supports_top_text, template_supports_bottom_text]):
-            required_text = "top" if template_supports_top_text else "bottom"
-            logger.error(
-                f"Template {args.template} requires the --{required_text}-text argument"
-            )
-        sys.exit(1)
-
-    # Transform the text
-    # fmt:off
-    top_text = args.top_text.upper() if args.top_text and (not args.keep_case) else args.top_text
-    bottom_text = args.bottom_text.upper() if args.bottom_text and (not args.keep_case) else args.bottom_text
-    top_text = top_text.replace("\\n", "\n").replace("\\N", "\n") if top_text else None
-    bottom_text = bottom_text.replace("\\n", "\n").replace("\\N", "\n") if bottom_text else None
-    # fmt: on
-
-    # Load the image
-    image = Image.open(template.image_path)
-
-    # Render the text
-    if top_text:
-        render_text_on_image(image, top_text, "top", template.config)
-    if bottom_text:
-        render_text_on_image(image, bottom_text, "bottom", template.config)
-
-    # Build the output path
-    output_path = (
-        Path(args.output)
-        if args.output
-        else (
-            DEFAULT_OUTPUT_DIR
-            / f"meme-{datetime.now().strftime('%Y-%m-%d-%H-%M-%S')}.{args.template}.png"
-        )
-    )
-    output_path.parent.mkdir(parents=True, exist_ok=True)
-
-    # Save the image
-    image.save(output_path)
-
-    # Show the image
-    if not args.no_show:
-        subprocess.run(["xdg-open", str(output_path)])
-
-    return 0
-
-
-if __name__ == "__main__":
-    sys.exit(main())
diff --git a/mini_apps/memegen/data/fonts/impact.ttf b/mini_apps/memegen/data/fonts/impact.ttf
deleted file mode 100644
index 114e6c1..0000000
Binary files a/mini_apps/memegen/data/fonts/impact.ttf and /dev/null differ
diff --git a/mini_apps/memegen/data/templates/bernie-asking/config.json b/mini_apps/memegen/data/templates/bernie-asking/config.json
deleted file mode 100644
index 5a2f0f4..0000000
--- a/mini_apps/memegen/data/templates/bernie-asking/config.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-    "font": "impact.ttf",
-    "fill_color": [255, 255, 255],
-    "stroke_color": [0, 0, 0],
-    "stroke_width": 2,
-    "zones": {
-        "bottom": {
-            "horizontal_align": "center",
-            "horizontal_offset": 0,
-            "vertical_align": "bottom",
-            "vertical_offset": -50,
-            "width": "80%",
-            "max_line_height": 50,
-            "line_spacing": 5
-        }
-    }
-}
\ No newline at end of file
diff --git a/mini_apps/memegen/data/templates/bernie-asking/template.png b/mini_apps/memegen/data/templates/bernie-asking/template.png
deleted file mode 100644
index 0d19bb4..0000000
Binary files a/mini_apps/memegen/data/templates/bernie-asking/template.png and /dev/null differ
diff --git a/mini_apps/memegen/data/templates/hotline-bling/config.json b/mini_apps/memegen/data/templates/hotline-bling/config.json
deleted file mode 100644
index cded53e..0000000
--- a/mini_apps/memegen/data/templates/hotline-bling/config.json
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-    "font": "impact.ttf",
-    "fill_color": [
-        0,
-        0,
-        0
-    ],
-    "stroke_color": [
-        0,
-        0,
-        0
-    ],
-    "stroke_width": 0,
-    "zones": {
-        "top": {
-            "horizontal_align": "right",
-            "horizontal_offset": -5,
-            "vertical_align": "top",
-            "vertical_offset": 75,
-            "width": "45%",
-            "max_line_height": 50,
-            "line_spacing": 5
-        },
-        "bottom": {
-            "horizontal_align": "right",
-            "horizontal_offset": -5,
-            "vertical_align": "bottom",
-            "vertical_offset": -75,
-            "width": "45%",
-            "max_line_height": 50,
-            "line_spacing": 5
-        }
-    }
-}
\ No newline at end of file
diff --git a/mini_apps/memegen/data/templates/hotline-bling/template.png b/mini_apps/memegen/data/templates/hotline-bling/template.png
deleted file mode 100644
index 7002060..0000000
Binary files a/mini_apps/memegen/data/templates/hotline-bling/template.png and /dev/null differ
diff --git a/mini_apps/memegen/data/templates/megamind/config.json b/mini_apps/memegen/data/templates/megamind/config.json
deleted file mode 100644
index 7ca4dbc..0000000
--- a/mini_apps/memegen/data/templates/megamind/config.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-    "font": "impact.ttf",
-    "fill_color": [255, 255, 255],
-    "stroke_color": [0, 0, 0],
-    "stroke_width": 2,
-    "zones": {
-        "top": {
-            "horizontal_align": "center",
-            "horizontal_offset": 0,
-            "vertical_align": "top",
-            "vertical_offset": 5,
-            "width": "80%",
-            "max_line_height": 80,
-            "line_spacing": 5
-        }
-    }
-}
\ No newline at end of file
diff --git a/mini_apps/memegen/data/templates/megamind/template.png b/mini_apps/memegen/data/templates/megamind/template.png
deleted file mode 100644
index 52bf5e5..0000000
Binary files a/mini_apps/memegen/data/templates/megamind/template.png and /dev/null differ