feat: app icon generator, signed app bundle script with install option
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import AppKit
|
||||
import SstUI
|
||||
import SwiftUI
|
||||
|
||||
/// The app icon: a brushed-aluminium plate with a VU meter and the red record lamp, drawn with the design tokens.
|
||||
/// Canvas is 256 pt; the macOS icon grid puts the plate at 206 pt with a 46 pt corner radius.
|
||||
struct IconView: View {
|
||||
static let canvas: CGFloat = 256
|
||||
private let plate: CGFloat = 206
|
||||
private let corner: CGFloat = 46
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
BrushedBackground()
|
||||
.frame(width: plate, height: plate)
|
||||
.clipShape(RoundedRectangle(cornerRadius: corner, style: .continuous))
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: corner, style: .continuous)
|
||||
.strokeBorder(Palette.lineSeamDark, lineWidth: Border.hairline))
|
||||
.shadow(color: .black.opacity(Opacity.mark), radius: Space.s3, y: Space.s2)
|
||||
|
||||
VStack(spacing: Space.s5) {
|
||||
VUMeterView(levelDbfs: -26, isActive: true, brand: "", initialFraction: 0.62)
|
||||
.frame(width: 150)
|
||||
HStack(spacing: Space.s4) {
|
||||
LampView(isOn: true, large: true).scaleEffect(1.5)
|
||||
SilkLabel("REC", style: Typography.heading)
|
||||
}
|
||||
}
|
||||
.offset(y: Space.s2)
|
||||
}
|
||||
.frame(width: Self.canvas, height: Self.canvas)
|
||||
.environment(\.colorScheme, .light)
|
||||
}
|
||||
}
|
||||
|
||||
@main
|
||||
@MainActor
|
||||
struct IconMaker {
|
||||
static func main() {
|
||||
guard CommandLine.arguments.count > 1 else {
|
||||
print("usage: SstIcon <output-folder.iconset>")
|
||||
exit(2)
|
||||
}
|
||||
let folder = URL(fileURLWithPath: CommandLine.arguments[1], isDirectory: true)
|
||||
try? FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
|
||||
|
||||
// (file name, pixel size) as iconutil expects them
|
||||
let outputs: [(String, CGFloat)] = [
|
||||
("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),
|
||||
]
|
||||
for (name, pixels) in outputs {
|
||||
let renderer = ImageRenderer(content: IconView())
|
||||
renderer.scale = pixels / IconView.canvas
|
||||
guard let tiff = renderer.nsImage?.tiffRepresentation,
|
||||
let png = NSBitmapImageRep(data: tiff)?.representation(using: .png, properties: [:])
|
||||
else {
|
||||
print("kon \(name) niet renderen")
|
||||
exit(1)
|
||||
}
|
||||
try? png.write(to: folder.appending(path: "\(name).png"))
|
||||
}
|
||||
print("iconset geschreven naar \(folder.path)")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user