diff --git a/generate_icons.py b/generate_icons.py new file mode 100644 index 0000000..6579d2b --- /dev/null +++ b/generate_icons.py @@ -0,0 +1,78 @@ +import math +from PIL import Image, ImageDraw + +def create_icons(output_dir): + size = 256 + img = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + + # Draw linear gradient on rounded rectangle + # Gradient: (31, 111, 235) to (137, 87, 229) + mask = Image.new("L", (size, size), 0) + draw_mask = ImageDraw.Draw(mask) + radius = int(size * 0.24) + draw_mask.rounded_rectangle([(0, 0), (size - 1, size - 1)], radius=radius, fill=255) + + grad = Image.new("RGBA", (size, size), (0, 0, 0, 0)) + draw_grad = ImageDraw.Draw(grad) + for y in range(size): + for x in range(size): + t = (x + y) / (2 * size) + r = int(31 * (1 - t) + 137 * t) + g = int(111 * (1 - t) + 87 * t) + b = int(235 * (1 - t) + 229 * t) + draw_grad.point((x, y), fill=(r, g, b, 255)) + + img = Image.composite(grad, img, mask) + + # Draw the white link chain icon in center + # Scale from 24x24 viewBox to fit nicely (about 130x130) + draw = ImageDraw.Draw(img) + stroke_w = int(size * 0.08) + + # To draw smooth link curves, we draw at 4x and downsample + hi_size = size * 4 + hi_img = Image.new("RGBA", (hi_size, hi_size), (0, 0, 0, 0)) + hi_draw = ImageDraw.Draw(hi_img) + + # Center = (hi_size/2, hi_size/2) + # The SVG path: + # Top-right link loop: center around (hi_size*0.58, hi_size*0.42) + # Bottom-left link loop: center around (hi_size*0.42, hi_size*0.58) + # We can draw the rounded pills rotated by 45 degrees + + # Create rotated pill for upper right link + link_w = int(hi_size * 0.28) + link_h = int(hi_size * 0.52) + pill = Image.new("RGBA", (link_w, link_h), (0, 0, 0, 0)) + pdraw = ImageDraw.Draw(pill) + pw = int(hi_size * 0.075) + pdraw.rounded_rectangle([(0, 0), (link_w - 1, link_h - 1)], radius=link_w // 2, outline=(255, 255, 255, 255), width=pw) + + # Rotate pill 45 deg + rot_pill = pill.rotate(45, resample=Image.Resampling.BICUBIC, expand=True) + + # Paste upper right link + offset_x1 = int(hi_size * 0.38) + offset_y1 = int(hi_size * 0.16) + hi_img.alpha_composite(rot_pill, (offset_x1, offset_y1)) + + # Paste bottom left link + offset_x2 = int(hi_size * 0.16) + offset_y2 = int(hi_size * 0.38) + hi_img.alpha_composite(rot_pill, (offset_x2, offset_y2)) + + # Downscale hi_img with anti-aliasing + icon_layer = hi_img.resize((size, size), Image.Resampling.LANCZOS) + img.alpha_composite(icon_layer) + + # Save outputs + # 1. High-res PNG (256x256) + img.save(f"{output_dir}/favicon.png", "PNG") + img.save(f"{output_dir}/apple-touch-icon.png", "PNG") + + # 2. Multi-size ICO (16, 32, 48, 64) + img.save(f"{output_dir}/favicon.ico", format="ICO", sizes=[(16, 16), (32, 32), (48, 48), (64, 64)]) + print("Generated favicon.png, apple-touch-icon.png, favicon.ico successfully!") + +if __name__ == "__main__": + create_icons(r"C:\Users\Chris\.gemini\antigravity\scratch\thread-ripper\thread_ripper\static") diff --git a/thread_ripper/server.py b/thread_ripper/server.py index ad5c94d..b63ed3a 100644 --- a/thread_ripper/server.py +++ b/thread_ripper/server.py @@ -115,6 +115,14 @@ def download_thread(thread_id: str, format: str): def health_check(): return {"status": "ok", "app": "Thread Ripper", "version": "1.0.0"} +# Direct favicon endpoint for browsers requesting /favicon.ico +@app.get("/favicon.ico", include_in_schema=False) +def get_favicon(): + fav_file = os.path.join(static_dir, "favicon.ico") + if os.path.exists(fav_file): + return FileResponse(fav_file) + return Response(status_code=404) + # Mount static frontend directory static_dir = os.path.join(os.path.dirname(__file__), "static") if os.path.exists(static_dir): diff --git a/thread_ripper/static/apple-touch-icon.png b/thread_ripper/static/apple-touch-icon.png new file mode 100644 index 0000000..1fc81b7 Binary files /dev/null and b/thread_ripper/static/apple-touch-icon.png differ diff --git a/thread_ripper/static/favicon.ico b/thread_ripper/static/favicon.ico new file mode 100644 index 0000000..0c4dce1 Binary files /dev/null and b/thread_ripper/static/favicon.ico differ diff --git a/thread_ripper/static/favicon.png b/thread_ripper/static/favicon.png new file mode 100644 index 0000000..1fc81b7 Binary files /dev/null and b/thread_ripper/static/favicon.png differ diff --git a/thread_ripper/static/favicon.svg b/thread_ripper/static/favicon.svg new file mode 100644 index 0000000..e93e363 --- /dev/null +++ b/thread_ripper/static/favicon.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/thread_ripper/static/index.html b/thread_ripper/static/index.html index f7c49ea..34a92be 100644 --- a/thread_ripper/static/index.html +++ b/thread_ripper/static/index.html @@ -4,6 +4,11 @@ Thread Ripper - Stitch Threads into Single Posts + + + + +