diff --git a/apps/desktop/resources/icon.icns b/apps/desktop/resources/icon.icns index 64fa6d5be..de2a15fdc 100644 Binary files a/apps/desktop/resources/icon.icns and b/apps/desktop/resources/icon.icns differ diff --git a/apps/desktop/resources/icon.png b/apps/desktop/resources/icon.png index 1ca0add68..9705b51f2 100644 Binary files a/apps/desktop/resources/icon.png and b/apps/desktop/resources/icon.png differ diff --git a/apps/marketing/public/apple-touch-icon.png b/apps/marketing/public/apple-touch-icon.png new file mode 100644 index 000000000..6d7cbd518 Binary files /dev/null and b/apps/marketing/public/apple-touch-icon.png differ diff --git a/apps/marketing/public/favicon-16x16.png b/apps/marketing/public/favicon-16x16.png new file mode 100644 index 000000000..596de4f7c Binary files /dev/null and b/apps/marketing/public/favicon-16x16.png differ diff --git a/apps/marketing/public/favicon-32x32.png b/apps/marketing/public/favicon-32x32.png new file mode 100644 index 000000000..a876ac057 Binary files /dev/null and b/apps/marketing/public/favicon-32x32.png differ diff --git a/apps/marketing/public/favicon.ico b/apps/marketing/public/favicon.ico new file mode 100644 index 000000000..7cdc5b602 Binary files /dev/null and b/apps/marketing/public/favicon.ico differ diff --git a/apps/marketing/public/icon.png b/apps/marketing/public/icon.png new file mode 100644 index 000000000..85c74f7cd Binary files /dev/null and b/apps/marketing/public/icon.png differ diff --git a/assets/dev/okcode-dev-ios-1024.png b/assets/dev/okcode-dev-ios-1024.png index 85c74f7cd..869f5523f 100644 Binary files a/assets/dev/okcode-dev-ios-1024.png and b/assets/dev/okcode-dev-ios-1024.png differ diff --git a/assets/dev/okcode-dev-macos-1024.png b/assets/dev/okcode-dev-macos-1024.png index 85c74f7cd..869f5523f 100644 Binary files a/assets/dev/okcode-dev-macos-1024.png and b/assets/dev/okcode-dev-macos-1024.png differ diff --git a/assets/prod/okcode-ios-1024.png b/assets/prod/okcode-ios-1024.png index 85c74f7cd..869f5523f 100644 Binary files a/assets/prod/okcode-ios-1024.png and b/assets/prod/okcode-ios-1024.png differ diff --git a/assets/prod/okcode-macos-1024.png b/assets/prod/okcode-macos-1024.png index 85c74f7cd..869f5523f 100644 Binary files a/assets/prod/okcode-macos-1024.png and b/assets/prod/okcode-macos-1024.png differ diff --git a/scripts/generate-brand-assets.py b/scripts/generate-brand-assets.py index 3eb9c3652..de1d46597 100644 --- a/scripts/generate-brand-assets.py +++ b/scripts/generate-brand-assets.py @@ -10,11 +10,12 @@ from __future__ import annotations +import math import sys from pathlib import Path try: - from PIL import Image + from PIL import Image, ImageDraw except ImportError as e: print("Install Pillow: python3 -m pip install pillow", file=sys.stderr) raise SystemExit(1) from e @@ -26,11 +27,62 @@ ICO_SIZES_WEB = (16, 32, 48) ICO_SIZES_DESKTOP = (16, 32, 48, 64, 128, 256) +# macOS icon corner radius as a fraction of icon size (~22.37% matches Apple's squircle) +MACOS_CORNER_RADIUS_FRACTION = 0.2237 + def resize(img: Image.Image, size: int) -> Image.Image: return img.resize((size, size), Image.Resampling.LANCZOS) +def _superellipse_mask(size: int, radius_fraction: float = MACOS_CORNER_RADIUS_FRACTION) -> Image.Image: + """Create a macOS-style continuous-curvature rounded rectangle (squircle) mask. + + Uses a superellipse approximation (n≈5) which closely matches Apple's + smoothed corner shape used in macOS Big Sur and later. + """ + scale = 4 # supersampled for antialiasing + s = size * scale + r = int(s * radius_fraction) + mask = Image.new("L", (s, s), 0) + draw = ImageDraw.Draw(mask) + # Draw the main body (cross shape) and corner arcs + # Center rectangle (full width, excluding corner rows) + draw.rectangle([0, r, s - 1, s - 1 - r], fill=255) + # Top/bottom strips (excluding corner columns) + draw.rectangle([r, 0, s - 1 - r, r], fill=255) + draw.rectangle([r, s - 1 - r, s - 1 - r, s - 1], fill=255) + + # Draw smooth corners using superellipse (n=5 for Apple-like smoothness) + n = 5.0 + for cy, cx in [(r, r), (r, s - 1 - r), (s - 1 - r, r), (s - 1 - r, s - 1 - r)]: + for dy in range(-r, r + 1): + for dx in range(-r, r + 1): + # Normalise to [-1, 1] range within the corner radius + nx = abs(dx) / r if r > 0 else 0 + ny = abs(dy) / r if r > 0 else 0 + if nx ** n + ny ** n <= 1.0: + px, py = cx + dx, cy + dy + if 0 <= px < s and 0 <= py < s: + mask.putpixel((px, py), 255) + + # Downsample for antialiased edges + return mask.resize((size, size), Image.Resampling.LANCZOS) + + +def apply_macos_mask(img: Image.Image) -> Image.Image: + """Apply the macOS squircle mask to an image, making corners transparent.""" + size = img.width + img = img.convert("RGBA") + mask = _superellipse_mask(size) + # Apply mask to alpha channel + r, g, b, a = img.split() + # Composite: keep existing alpha where mask is white, transparent where mask is black + from PIL import ImageChops + a = ImageChops.multiply(a, mask.convert("L")) + return Image.merge("RGBA", (r, g, b, a)) + + def save_ico(path: Path, source: Image.Image, sizes: tuple[int, ...]) -> None: path.parent.mkdir(parents=True, exist_ok=True) source.save( @@ -59,10 +111,16 @@ def main() -> None: mark_1024.save(prod_dir / "okcode-mark-1024.png") mark_1024.save(dev_dir / "okcode-dev-mark-1024.png") - for name in ("okcode-macos-1024.png", "okcode-linux-1024.png", "okcode-ios-1024.png"): - mark_1024.save(prod_dir / name) - for name in ("okcode-dev-macos-1024.png", "okcode-dev-universal-1024.png", "okcode-dev-ios-1024.png"): - mark_1024.save(dev_dir / name) + # macOS & iOS icons get the squircle mask; Linux stays square + macos_1024 = apply_macos_mask(mark_1024) + macos_1024.save(prod_dir / "okcode-macos-1024.png") + macos_1024.save(prod_dir / "okcode-ios-1024.png") + mark_1024.save(prod_dir / "okcode-linux-1024.png") + + macos_dev_1024 = apply_macos_mask(mark_1024) + macos_dev_1024.save(dev_dir / "okcode-dev-macos-1024.png") + macos_dev_1024.save(dev_dir / "okcode-dev-ios-1024.png") + mark_1024.save(dev_dir / "okcode-dev-universal-1024.png") # Web PNGs (match prior naming: 16/32 favicon + separate apple-touch) resize(img, 16).save(prod_dir / "okcode-web-favicon-16x16.png")