Skip to content

Instantly share code, notes, and snippets.

@leogdion
Created March 20, 2026 23:39
Show Gist options
  • Select an option

  • Save leogdion/f9330d9f624c6b623306648bfe23c8f5 to your computer and use it in GitHub Desktop.

Select an option

Save leogdion/f9330d9f624c6b623306648bfe23c8f5 to your computer and use it in GitHub Desktop.
Export Icon Composer Icons for Press Kits and Web Sites
#!/bin/bash
# MonthBar Icon Exporter
# Exports all app icon renditions from Resources/AppIcon.icon:
# Marketing/Web/app-icon/ ← PNG + WebP at 1024px and 512px for all renditions
# ← Favicons (180, 32, 16px) and OG image (1200×630)
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Constants
ICON_COMPOSER="/Applications/Icon Composer.app"
ICTOOL="$ICON_COMPOSER/Contents/Executables/ictool"
ICON_SOURCE="$PROJECT_ROOT/Resources/AppIcon.icon"
WEB_OUTPUT_DIR="$PROJECT_ROOT/Marketing/Web"
ICON_OUTPUT_DIR="$WEB_OUTPUT_DIR/app-icon"
# Tint colors for ictool (hue value 0.0–1.0)
TINT_BLUE="0.60" # Brand blue #0088FF
TINT_GREEN="0.50" # Progress green #34C759
TINT_STRENGTH="0.75"
TINT_COLORS=("blue:$TINT_BLUE" "green:$TINT_GREEN")
# Rendition → filename-suffix mapping
RENDITIONS=(
"Default:icon"
"Dark:icon-dark"
"ClearLight:icon-clear-light"
"ClearDark:icon-clear-dark"
"TintedLight:icon-tinted-light"
"TintedDark:icon-tinted-dark"
)
# Helper: run Python with Pillow via uv
run_pillow() {
mise exec -- uv run --with Pillow python3 "$@"
}
echo "MonthBar Icon Exporter"
echo ""
# Step 1: Validate
echo "1. Validating..."
if [ ! -f "$ICTOOL" ]; then
echo " Error: ictool not found at $ICTOOL"
echo " Ensure Icon Composer is installed at $ICON_COMPOSER"
exit 1
fi
if [ ! -d "$ICON_SOURCE" ]; then
echo " Error: Icon source not found at $ICON_SOURCE"
exit 1
fi
echo " ictool and icon source found"
# Step 2: Set up output directory
echo ""
echo "2. Setting up output directory..."
rm -rf "$ICON_OUTPUT_DIR"
mkdir -p "$ICON_OUTPUT_DIR"
echo " $ICON_OUTPUT_DIR ready"
# Step 3: Export icons (all 6 appearances at 1024px and 512px)
echo ""
echo "3. Exporting app icons..."
export_icon() {
local rendition="$1" suffix="$2" size="$3"
local tint_args=("${@:4}")
local png_file="$ICON_OUTPUT_DIR/${suffix}-${size}.png"
local webp_file="$ICON_OUTPUT_DIR/${suffix}-${size}.webp"
"$ICTOOL" "$ICON_SOURCE" --export-image --output-file "$png_file" \
--platform macOS --rendition "$rendition" \
--width "$size" --height "$size" --scale 1 "${tint_args[@]}"
run_pillow -c "
import sys
from PIL import Image
Image.open(sys.argv[1]).save(sys.argv[2], 'WEBP')
" "$png_file" "$webp_file"
echo " ${suffix}-${size}.png"
echo " ${suffix}-${size}.webp"
}
for entry in "${RENDITIONS[@]}"; do
rendition="${entry%%:*}"
suffix="${entry##*:}"
case "$rendition" in
TintedLight|TintedDark)
for tint_entry in "${TINT_COLORS[@]}"; do
color_name="${tint_entry%%:*}"
color_hue="${tint_entry##*:}"
for size in 1024 512; do
export_icon "$rendition" "${suffix}-${color_name}" "$size" \
--tint-color "$color_hue" --tint-strength "$TINT_STRENGTH"
done
done
;;
*)
for size in 1024 512; do
export_icon "$rendition" "$suffix" "$size"
done
;;
esac
done
# Step 4: Generate favicons and OG image
echo ""
echo "4. Generating favicons and OG image..."
ICON_1024="$ICON_OUTPUT_DIR/icon-1024.png"
run_pillow -c "
import sys
from PIL import Image
Image.open(sys.argv[1]).resize((180, 180), Image.LANCZOS).save(sys.argv[2])
" "$ICON_1024" "$ICON_OUTPUT_DIR/apple-touch-icon.png"
echo " apple-touch-icon.png (180×180)"
run_pillow -c "
import sys
from PIL import Image
Image.open(sys.argv[1]).resize((32, 32), Image.LANCZOS).save(sys.argv[2])
" "$ICON_1024" "$ICON_OUTPUT_DIR/favicon-32x32.png"
echo " favicon-32x32.png (32×32)"
run_pillow -c "
import sys
from PIL import Image
Image.open(sys.argv[1]).resize((16, 16), Image.LANCZOS).save(sys.argv[2])
" "$ICON_1024" "$ICON_OUTPUT_DIR/favicon-16x16.png"
echo " favicon-16x16.png (16×16)"
run_pillow -c "
import sys
from PIL import Image
icon = Image.open(sys.argv[1]).resize((630, 630), Image.LANCZOS)
og = Image.new('RGBA', (1200, 630), (0, 0, 0, 0))
og.paste(icon, ((1200 - 630) // 2, 0))
og.save(sys.argv[2])
og.save(sys.argv[3], 'WEBP')
" "$ICON_1024" "$ICON_OUTPUT_DIR/og-image.png" "$ICON_OUTPUT_DIR/og-image.webp"
echo " og-image.png (1200×630)"
echo " og-image.webp (1200×630)"
# Step 5: Summary
echo ""
echo "Complete! Generated files in Marketing/Web/app-icon/:"
echo ""
for entry in "${RENDITIONS[@]}"; do
rendition="${entry%%:*}"
suffix="${entry##*:}"
case "$rendition" in
TintedLight|TintedDark)
for tint_entry in "${TINT_COLORS[@]}"; do
color_name="${tint_entry%%:*}"
echo " ${suffix}-${color_name}-1024.png (1024×1024)"
echo " ${suffix}-${color_name}-1024.webp (1024×1024)"
echo " ${suffix}-${color_name}-512.png (512×512)"
echo " ${suffix}-${color_name}-512.webp (512×512)"
done
;;
*)
echo " ${suffix}-1024.png (1024×1024)"
echo " ${suffix}-1024.webp (1024×1024)"
echo " ${suffix}-512.png (512×512)"
echo " ${suffix}-512.webp (512×512)"
;;
esac
done
echo ""
echo " apple-touch-icon.png (180×180)"
echo " favicon-32x32.png (32×32)"
echo " favicon-16x16.png (16×16)"
echo " og-image.png (1200×630)"
echo " og-image.webp (1200×630)"
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment