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:
2026-10-01 15:41:20 +02:00
co-authored by Claude Sonnet 5.5
parent 15c9582e12
commit cfe8208e11
5 changed files with 113 additions and 8 deletions
Binary file not shown.
+1
View File
@@ -19,6 +19,7 @@ let package = Package(
]
),
.executableTarget(name: "Sst", dependencies: ["SstCore", "SstWhisper", "SstData", "SstUI"]),
.executableTarget(name: "SstIcon", dependencies: ["SstUI"]),
.executableTarget(name: "SstProbe", dependencies: ["SstCore", "SstWhisper"]),
.executableTarget(name: "SstGallery", dependencies: ["SstUI"]),
.testTarget(name: "SstCoreTests", dependencies: ["SstCore"]),
+67
View File
@@ -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)")
}
}
+35 -8
View File
@@ -1,10 +1,14 @@
#!/usr/bin/env bash
# Builds build/Sst.app (a normal Dock app, no LSUIElement). Pass --install to copy it to /Applications.
set -euo pipefail
cd "$(dirname "$0")/.."
APP="build/Sst.app"
IDENTIFIER="local.sst.app"
VERSION="0.2.0"
swift build -c release --product Sst
BIN=".build/release/Sst"
APP="build/Sst.app"
rm -rf "$APP"
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
@@ -13,26 +17,49 @@ cp "$BIN" "$APP/Contents/MacOS/Sst"
for b in .build/release/*.bundle; do
[ -e "$b" ] && cp -R "$b" "$APP/Contents/Resources/"
done
if [ -f Design/AppIcon.icns ]; then
cp Design/AppIcon.icns "$APP/Contents/Resources/AppIcon.icns"
else
echo "Let op: Design/AppIcon.icns ontbreekt; run scripts/make-icon.sh."
fi
cat > "$APP/Contents/Info.plist" <<'PLIST'
cat > "$APP/Contents/Info.plist" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key><string>local.sst.app</string>
<key>CFBundleIdentifier</key><string>$IDENTIFIER</string>
<key>CFBundleName</key><string>Sst</string>
<key>CFBundleDisplayName</key><string>Sst</string>
<key>CFBundleExecutable</key><string>Sst</string>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleShortVersionString</key><string>0.1.0</string>
<key>CFBundleShortVersionString</key><string>$VERSION</string>
<key>CFBundleVersion</key><string>1</string>
<key>CFBundleIconFile</key><string>AppIcon</string>
<key>LSMinimumSystemVersion</key><string>14.0</string>
<key>LSUIElement</key><true/>
<key>LSApplicationCategoryType</key><string>public.app-category.productivity</string>
<key>NSHighResolutionCapable</key><true/>
<key>NSMicrophoneUsageDescription</key>
<string>Sst neemt je stem op zolang je Fn ingedrukt houdt, om die lokaal naar tekst om te zetten.</string>
<string>Sst neemt je stem op zolang je de sneltoets ingedrukt houdt, om die lokaal naar tekst om te zetten.</string>
</dict>
</plist>
PLIST
codesign --force --sign - --identifier local.sst.app "$APP"
# A stable signing identity keeps the macOS permissions (Microphone, Accessibility, Input Monitoring) across rebuilds.
IDENTITY="${SST_SIGN_IDENTITY:-$(security find-identity -v -p codesigning | sed -n 's/.*"\(Apple Development: [^"]*\)".*/\1/p' | head -1)}"
if [ -n "$IDENTITY" ]; then
codesign --force --sign "$IDENTITY" --identifier "$IDENTIFIER" "$APP"
echo "Ondertekend met: $IDENTITY"
else
codesign --force --sign - --identifier "$IDENTIFIER" "$APP"
echo "WAARSCHUWING: geen Apple Development-certificaat gevonden; ad-hoc ondertekend (rechten gaan na elke herbouw verloren)."
fi
codesign --verify --strict "$APP"
echo "Gebouwd: $APP"
echo "Na een herbouw: tccutil reset Accessibility local.sst.app; tccutil reset ListenEvent local.sst.app; tccutil reset Microphone local.sst.app (en geef de rechten opnieuw)."
echo "Eenmalig na het wisselen van ondertekening: tccutil reset Accessibility $IDENTIFIER; tccutil reset ListenEvent $IDENTIFIER; tccutil reset Microphone $IDENTIFIER (en geef de rechten opnieuw)."
if [ "${1:-}" = "--install" ]; then
rm -rf /Applications/Sst.app
cp -R "$APP" /Applications/Sst.app
echo "Geïnstalleerd: /Applications/Sst.app"
fi
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# Renders the app icon from the design tokens and stores it as Design/AppIcon.icns (committed).
set -euo pipefail
cd "$(dirname "$0")/.."
swift build --product SstIcon
rm -rf build/AppIcon.iconset
.build/debug/SstIcon build/AppIcon.iconset
iconutil -c icns build/AppIcon.iconset -o Design/AppIcon.icns
echo "Icoon geschreven: Design/AppIcon.icns"