Skip to content

Instantly share code, notes, and snippets.

@arky
Last active April 21, 2026 10:36
Show Gist options
  • Select an option

  • Save arky/70dfb3ad750bace4132839ba9cd718e0 to your computer and use it in GitHub Desktop.

Select an option

Save arky/70dfb3ad750bace4132839ba9cd718e0 to your computer and use it in GitHub Desktop.
Building Iconforge, a Shiny app that uploads images, resizes them, compresses them, and outputs a base64 HTML img tag for Quicklink icons.
import base64
import io
import json
import os
import shinyswatch
from PIL import Image
from shiny import reactive
from shiny.express import input, render, ui
from shiny.types import FileInfo, SilentException
ui.page_opts(title="Iconforge", theme=shinyswatch.theme.darkly())
MIME_MAP = {
"ICO": "image/x-icon",
"JPEG": "image/jpeg",
"JPG": "image/jpeg",
"PNG": "image/png",
"GIF": "image/gif",
"WEBP": "image/webp",
}
SIZE_CHOICES = {"16": "16x16", "32": "32x32", "48": "48x48", "256": "256x256"}
def format_bytes(n: int) -> str:
if n < 1024:
return f"{n} B"
elif n < 1024 * 1024:
return f"{n / 1024:.1f} KB"
else:
return f"{n / 1024 / 1024:.1f} MB"
@reactive.calc
def processed():
file_infos: list[FileInfo] = input.file()
if not file_infos:
raise SilentException()
file_info = file_infos[0]
original_size = os.path.getsize(file_info["datapath"])
px = int(input.output_size())
try:
pil_img = Image.open(file_info["datapath"])
pil_img.verify()
except Exception:
raise ValueError(f"'{file_info['name']}' is not a recognised image format.")
with Image.open(file_info["datapath"]) as img:
fmt = img.format
if not fmt or fmt not in MIME_MAP:
raise ValueError(f"Unsupported image format: {fmt or 'unknown'}.")
img = img.resize((px, px), Image.LANCZOS)
buffer = io.BytesIO()
if fmt in ("JPEG", "JPG"):
img.save(buffer, format="JPEG", optimize=True, quality=85)
else:
img.save(buffer, format=fmt, optimize=True)
compressed_data = buffer.getvalue()
compressed_size = len(compressed_data)
savings = original_size - compressed_size
savings_pct = (savings / original_size * 100) if original_size > 0 else 0
mime = MIME_MAP.get(fmt, f"image/{fmt.lower()}")
b64 = base64.b64encode(compressed_data).decode()
return {
"original_size": original_size,
"compressed_size": compressed_size,
"savings": savings,
"savings_pct": savings_pct,
"mime": mime,
"b64": b64,
"px": px,
}
with ui.layout_columns(col_widths={"sm": (2, 8, 2)}):
ui.HTML("")
with ui.card():
# Header
ui.p(
"Upload an image, resize and compress it, then copy the base64 "
"JSON code for use as a Quicklink icon attributes.",
class_="text-muted small mb-3",
)
# Inputs
ui.input_file("file", "Choose an image to upload:", multiple=False)
ui.input_radio_buttons(
"output_size",
"Output size:",
choices=SIZE_CHOICES,
selected="48",
inline=True,
)
with ui.layout_columns(col_widths=(6, 6)):
ui.input_text("icon_id", "ID:", placeholder="your-icon-id")
ui.input_text("icon_title", "Title:", placeholder="your-icon-title")
ui.input_checkbox("icon_default", "Default", value=True)
ui.hr()
# Preview + size stats side by side
@render.ui
def results():
try:
data = processed()
except ValueError as e:
return ui.div({"class": "alert alert-danger"}, str(e))
orig = format_bytes(data["original_size"])
comp = format_bytes(data["compressed_size"])
savings = format_bytes(abs(data["savings"]))
pct = data["savings_pct"]
label = "Savings" if data["savings"] >= 0 else "Increase"
px = data["px"]
preview = ui.img(
src=f"data:{data['mime']};base64,{data['b64']}",
style=f"width:{px}px;height:{px}px;",
)
stats = ui.tags.table(
{"class": "table table-sm table-borderless w-auto mb-0"},
ui.tags.tr(ui.tags.td("Original:"), ui.tags.td({"class": "ps-3"}, orig)),
ui.tags.tr(ui.tags.td("Compressed:"), ui.tags.td({"class": "ps-3"}, comp)),
ui.tags.tr(
ui.tags.td(f"{label}:"),
ui.tags.td({"class": "ps-3"}, f"{savings} ({abs(pct):.1f}%)"),
),
)
return ui.div(
{"class": "d-flex align-items-center gap-4 mb-3"},
preview,
stats,
)
# JSON output with copy button
# Credit Garrick of Shiny for Python Developer Team!
@render.ui
def html_out():
try:
data = processed()
except ValueError:
raise SilentException()
payload = {
"id": input.icon_id() or "your-icon-id",
"icon": {
"src": f"data:{data['mime']};base64,{data['b64']}",
"height": data["px"],
},
"title": input.icon_title() or "your-icon-title",
"default": input.icon_default(),
}
json_out = json.dumps(payload, indent=2)
return ui.div(
ui.tags.button(
"Copy",
onclick=(
"navigator.clipboard.writeText("
"document.getElementById('img-tag-code').innerText"
").then(() => {"
" this.textContent = 'Copied!';"
" setTimeout(() => this.textContent = 'Copy', 1500);"
"})"
),
class_="btn btn-sm btn-outline-secondary mb-2 float-end",
),
ui.pre(ui.code({"id": "img-tag-code"}, json_out)),
)
ui.HTML("")
shiny
shinyswatch
Pillow
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment