#!/usr/bin/env swift // Generiert das RemminaMac App-Icon (Dracula-Terminal) als PNG-Set + .iconset. // Aufruf: swift scripts/RenderIcon.swift // Braucht nur macOS-Bordmittel (AppKit). Kein Netzwerk, keine Deps. import AppKit import Foundation let outDir = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : "/tmp/RemminaMac.iconset" try? FileManager.default.createDirectory(atPath: outDir, withIntermediateDirectories: true) // Dracula-Palette func c(_ hex: String, _ alpha: CGFloat = 1) -> NSColor { var v: UInt64 = 0 Scanner(string: hex).scanHexInt64(&v) return NSColor(red: CGFloat((v >> 16) & 0xFF) / 255, green: CGFloat((v >> 8) & 0xFF) / 255, blue: CGFloat(v & 0xFF) / 255, alpha: alpha) } let bg1 = c("3A3D4F"), bg2 = c("23242F"), bar = c("44475A") let green = c("50FA7B"), pink = c("FF79C6"), purple = c("BD93F9") let red = c("FF5555"), yellow = c("F1FA8C") func drawIcon(size: CGFloat) -> NSImage { let img = NSImage(size: NSSize(width: size, height: size)) img.lockFocus() let s = size / 1024.0 // Skalierung relativ zu 1024er-Master NSGraphicsContext.current?.imageInterpolation = .high // Hintergrund: abgerundetes Quadrat mit Verlauf let rect = NSRect(x: 0, y: 0, width: size, height: size) let radius = 232 * s let path = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius) let grad = NSGradient(colors: [bg1, bg2])! grad.draw(in: path, angle: 90) // feine Kontur bar.withAlphaComponent(0.9).setStroke() path.lineWidth = max(2, 6 * s) path.stroke() // Dezenter Glow hinter der Glyphe let glow = NSShadow() glow.shadowColor = purple.withAlphaComponent(0.55) glow.shadowBlurRadius = 90 * s glow.shadowOffset = .zero // Traffic-Lights (Fenster-Look) for (i, col) in [red, yellow, green].enumerated() { let r = 40 * s let cx = CGFloat(150 + i * 108) * s let cy = size - 158.0 * s col.setFill() NSBezierPath(ovalIn: NSRect(x: cx - r, y: cy - r, width: 2 * r, height: 2 * r)).fill() } // Prompt „>" + Block-Cursor (Menlo Bold) let fontSize: CGFloat = 470 * s let font: NSFont = NSFont(name: "Menlo-Bold", size: fontSize) ?? NSFont.monospacedSystemFont(ofSize: fontSize, weight: .bold) let attrs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: green] let glyph = NSAttributedString(string: ">", attributes: attrs) let gw = glyph.size() // Cursor-Block rechts vom „>" let gap = 44 * s let cursorW = 128 * s, cursorH = 96 * s let totalW = gw.width + gap + cursorW let startX = (size - totalW) / 2 let baseY = size * 0.30 NSGraphicsContext.saveGraphicsState() NSGraphicsContext.current?.cgContext.setShadow(offset: .zero, blur: 90 * s, color: purple.withAlphaComponent(0.55).cgColor) glyph.draw(at: NSPoint(x: startX, y: baseY)) NSGraphicsContext.restoreGraphicsState() let cursor = NSBezierPath(roundedRect: NSRect(x: startX + gw.width + gap, y: baseY + 30 * s, width: cursorW, height: cursorH), xRadius: 14 * s, yRadius: 14 * s) pink.setFill() cursor.fill() img.unlockFocus() return img } func save(_ img: NSImage, _ name: String) { guard let tiff = img.tiffRepresentation, let rep = NSBitmapImageRep(data: tiff), let png = rep.representation(using: .png, properties: [:]) else { fputs("FEHLER bei \(name)\n", stderr); exit(1) } try! png.write(to: URL(fileURLWithPath: "\(outDir)/\(name)")) } // iconset: je Größe 1x + 2x for base in [16, 32, 128, 256, 512] { save(drawIcon(size: CGFloat(base)), "icon_\(base)x\(base).png") save(drawIcon(size: CGFloat(base * 2)), "icon_\(base)x\(base)@2x.png") } // Vorschau in groß try? FileManager.default.createDirectory(atPath: "/tmp", withIntermediateDirectories: true) save(drawIcon(size: 512), "RemminaMac-preview-tmp.png") print("iconset fertig: \(outDir)")