#!/usr/bin/env swift import AppKit import Foundation // Renders AppIcon.icns from code — no design tool, no binary asset to keep in sync. // Run: swift Tools/makeicon.swift // Edison Voice: an amber lightbulb on a deep slate tile. let background = NSColor(srgbRed: 0.13, green: 0.14, blue: 0.17, alpha: 1) let bulbColor = NSColor(srgbRed: 1.00, green: 0.78, blue: 0.28, alpha: 1) func drawIcon(size: CGFloat) -> NSImage { let image = NSImage(size: NSSize(width: size, height: size)) image.lockFocus() guard let ctx = NSGraphicsContext.current?.cgContext else { image.unlockFocus() return image } ctx.setShouldAntialias(true) ctx.interpolationQuality = .high // macOS Big Sur+ icon grid: art occupies the middle ~82%, leaving the shadow gutter. let inset = size * 0.09 let rect = CGRect(x: inset, y: inset, width: size - inset * 2, height: size - inset * 2) // Apple's squircle is ~22.37% of the tile's edge. let radius = rect.width * 0.2237 let squircle = CGPath(roundedRect: rect, cornerWidth: radius, cornerHeight: radius, transform: nil) // Drop shadow under the tile. ctx.saveGState() ctx.setShadow( offset: CGSize(width: 0, height: -size * 0.012), blur: size * 0.035, color: NSColor.black.withAlphaComponent(0.32).cgColor ) ctx.addPath(squircle) ctx.setFillColor(background.cgColor) ctx.fillPath() ctx.restoreGState() // Soft top highlight so the tile reads as glass rather than flat fill. ctx.saveGState() ctx.addPath(squircle) ctx.clip() let highlight = CGGradient( colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: [ NSColor.white.withAlphaComponent(0.10).cgColor, NSColor.white.withAlphaComponent(0.0).cgColor, ] as CFArray, locations: [0, 1] )! ctx.drawLinearGradient( highlight, start: CGPoint(x: rect.midX, y: rect.maxY), end: CGPoint(x: rect.midX, y: rect.midY), options: [] ) ctx.restoreGState() // Lightbulb glyph, tinted amber. guard let base = NSImage(systemSymbolName: "lightbulb.fill", accessibilityDescription: nil) else { image.unlockFocus() return image } let pointSize = rect.width * 0.52 let config = NSImage.SymbolConfiguration(pointSize: pointSize, weight: .semibold) .applying(NSImage.SymbolConfiguration(hierarchicalColor: bulbColor)) let bulb = base.withSymbolConfiguration(config) ?? base // Center the glyph. SF Symbols report their baseline-aware size, so frame it by its own // intrinsic size rather than assuming a square. let glyphSize = bulb.size let drawRect = CGRect( x: rect.midX - glyphSize.width / 2, y: rect.midY - glyphSize.height / 2, width: glyphSize.width, height: glyphSize.height ) ctx.saveGState() ctx.setShadow( offset: CGSize(width: 0, height: -size * 0.006), blur: size * 0.02, color: NSColor.black.withAlphaComponent(0.30).cgColor ) bulb.draw(in: drawRect) ctx.restoreGState() image.unlockFocus() return image } func png(_ image: NSImage, pixels: Int) -> Data? { guard let rep = NSBitmapImageRep( bitmapDataPlanes: nil, pixelsWide: pixels, pixelsHigh: pixels, bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0 ) else { return nil } rep.size = NSSize(width: pixels, height: pixels) NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: rep) // Redraw at native pixel size rather than scaling a single render — keeps the small // sizes crisp instead of muddy. drawIcon(size: CGFloat(pixels)).draw( in: NSRect(x: 0, y: 0, width: pixels, height: pixels), from: .zero, operation: .sourceOver, fraction: 1 ) NSGraphicsContext.restoreGraphicsState() return rep.representation(using: .png, properties: [:]) } let fm = FileManager.default let root = URL(fileURLWithPath: fm.currentDirectoryPath) let iconset = root.appendingPathComponent("Resources/AppIcon.iconset") try? fm.removeItem(at: iconset) try fm.createDirectory(at: iconset, withIntermediateDirectories: true) // (point size, scale) pairs iconutil expects. let variants: [(Int, Int)] = [ (16, 1), (16, 2), (32, 1), (32, 2), (128, 1), (128, 2), (256, 1), (256, 2), (512, 1), (512, 2), ] for (points, scale) in variants { let pixels = points * scale guard let data = png(NSImage(), pixels: pixels) else { print("failed at \(pixels)px"); exit(1) } let suffix = scale == 2 ? "@2x" : "" let name = "icon_\(points)x\(points)\(suffix).png" try data.write(to: iconset.appendingPathComponent(name)) } print("wrote \(variants.count) PNGs to Resources/AppIcon.iconset")