// Generates a placeholder AppIcon.iconset — a plain rounded-square // background with the coat glyph on top. Deliberately not derived from // Claude's own icon assets (unlike the old version of this file, which // composited on top of Claude.app's icon) — this is a stand-in until a // real icon is designed, so it draws everything itself from vector shapes. // // Usage: // swift icon/generate-icon.swift icon/AppIcon.iconset // iconutil -c icns icon/AppIcon.iconset -o icon/AppIcon.icns // rm -rf icon/AppIcon.iconset import Cocoa // Renders at exact pixel dimensions via an explicit NSBitmapImageRep rather // than NSImage.lockFocus() — lockFocus rasterizes at the *main screen's* // backing scale factor, which would silently double every size on a Retina // display instead of producing the exact px asked for. func renderIcon(pixels: Int) -> Data { let s = CGFloat(pixels) 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 { fatalError("could not create bitmap rep") } rep.size = NSSize(width: s, height: s) NSGraphicsContext.saveGraphicsState() let ctx = NSGraphicsContext(bitmapImageRep: rep) NSGraphicsContext.current = ctx ctx?.imageInterpolation = .high // Base: a plain rounded-square background (macOS "squircle"-ish corner // radius), a neutral slate color — no borrowed assets. let bg = NSColor(calibratedRed: 0.30, green: 0.32, blue: 0.36, alpha: 1.0) let bgPath = NSBezierPath(roundedRect: NSRect(x: 0, y: 0, width: s, height: s), xRadius: s * 0.22, yRadius: s * 0.22) bg.setFill() bgPath.fill() // Foreground: the same coat glyph as before — an original shape, not // derived from any borrowed icon. let coat = NSColor(calibratedRed: 0.62, green: 0.44, blue: 0.24, alpha: 1.0) let coatShadow = NSColor(calibratedRed: 0.48, green: 0.33, blue: 0.16, alpha: 1.0) let midX = s * 0.5 let topY = s * 0.62 let collarOutX = s * 0.34 let collarInX = s * 0.11 let shoulderY = s * 0.53 let waistY = s * 0.30 let waistHalf = s * 0.30 let hemY = s * 0.06 let hemHalf = s * 0.40 let path = NSBezierPath() path.move(to: NSPoint(x: midX, y: topY)) path.line(to: NSPoint(x: midX - collarOutX, y: shoulderY)) path.line(to: NSPoint(x: midX - collarInX, y: shoulderY - s * 0.07)) path.line(to: NSPoint(x: midX - waistHalf, y: waistY)) path.line(to: NSPoint(x: midX - hemHalf, y: hemY)) path.line(to: NSPoint(x: midX + hemHalf, y: hemY)) path.line(to: NSPoint(x: midX + waistHalf, y: waistY)) path.line(to: NSPoint(x: midX + collarInX, y: shoulderY - s * 0.07)) path.line(to: NSPoint(x: midX + collarOutX, y: shoulderY)) path.close() coat.setFill() path.fill() // A thin darker strip down the centerline reads as the coat's front // seam/placket, breaking up the flat fill at larger sizes. let seam = NSBezierPath() seam.move(to: NSPoint(x: midX, y: topY)) seam.line(to: NSPoint(x: midX, y: hemY)) seam.lineWidth = max(s * 0.012, 1) coatShadow.setStroke() seam.stroke() let buttonRadius = max(s * 0.02, 0.75) for t: CGFloat in [0.20, 0.42, 0.64] { let y = hemY + (shoulderY - hemY) * t let r = NSRect(x: midX - buttonRadius, y: y - buttonRadius, width: buttonRadius * 2, height: buttonRadius * 2) coatShadow.setFill() NSBezierPath(ovalIn: r).fill() } ctx?.flushGraphics() NSGraphicsContext.restoreGraphicsState() guard let png = rep.representation(using: .png, properties: [:]) else { fatalError("failed to encode PNG at \(pixels)px") } return png } let sizes: [(name: String, px: Int)] = [ ("icon_16x16", 16), ("icon_16x16@2x", 32), ("icon_32x32", 32), ("icon_32x32@2x", 64), ("icon_128x128", 128), ("icon_128x128@2x", 256), ("icon_256x256", 256), ("icon_256x256@2x", 512), ("icon_512x512", 512), ("icon_512x512@2x", 1024), ] let outDir = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : "AppIcon.iconset" try? FileManager.default.createDirectory(atPath: outDir, withIntermediateDirectories: true) for (name, px) in sizes { let data = renderIcon(pixels: px) let path = "\(outDir)/\(name).png" try? data.write(to: URL(fileURLWithPath: path)) print("wrote \(path)") }