diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index dc9d8e0..ef3cd9c 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -68,6 +68,7 @@ jobs:
CFBundlePackageTypeAPPL
CFBundleShortVersionString$VERSION
CFBundleVersion$VERSION
+ CFBundleIconFileAppIcon
LSMinimumSystemVersion13.0
LSUIElement
NSHighResolutionCapable
@@ -75,6 +76,13 @@ jobs:
PLIST
+ # Rasterize the vector brand mark at every required size (NSImage renders the SVG as
+ # vector, not a blown-up 16px bitmap) and pack it into a proper multi-resolution .icns.
+ swift Scripts/generate-icon.swift \
+ NotchWatch/Resources/Images/notchwatch-corbeau-16-modified.svg \
+ AppIcon.iconset
+ iconutil -c icns AppIcon.iconset -o "$APP/Contents/Resources/AppIcon.icns"
+
# No Apple Developer ID available: ad-hoc sign only. Users get a Gatekeeper
# warning on first launch (documented in README) — not notarized.
codesign --force --deep --sign - "$APP"
diff --git a/README.md b/README.md
index 41a74ef..d165487 100644
--- a/README.md
+++ b/README.md
@@ -27,10 +27,11 @@
```sh
brew tap Lixeas/notchwatch https://github.com/Lixeas/NotchWatch
+brew trust --cask lixeas/notchwatch/notch-watch
brew install --cask notch-watch
```
-This installs the latest signed `.dmg` from [GitHub Releases](https://github.com/Lixeas/NotchWatch/releases) and keeps a Homebrew-managed `NotchWatch.app` in `/Applications`. To update: `brew upgrade --cask notch-watch`.
+Homebrew 6.0+ requires explicit trust for non-official taps (`brew trust`) — one-time, before the first install. This installs the latest signed `.dmg` from [GitHub Releases](https://github.com/Lixeas/NotchWatch/releases) and keeps a Homebrew-managed `NotchWatch.app` in `/Applications`. To update: `brew upgrade --cask notch-watch`.
### Option 2 — Manual download
diff --git a/Scripts/generate-icon.swift b/Scripts/generate-icon.swift
new file mode 100644
index 0000000..b6b427c
--- /dev/null
+++ b/Scripts/generate-icon.swift
@@ -0,0 +1,48 @@
+import AppKit
+
+// Standalone build-time script (run via `swift Scripts/generate-icon.swift `
+// on the macOS release runner) -- not part of the app target, iconutil needs a populated
+// .iconset directory of PNGs at each required size, generated here from the vector SVG source.
+
+let arguments = CommandLine.arguments
+guard arguments.count == 3 else {
+ FileHandle.standardError.write(Data("Usage: generate-icon.swift \n".utf8))
+ exit(1)
+}
+let sourcePath = arguments[1]
+let outputDir = arguments[2]
+
+guard let source = NSImage(contentsOfFile: sourcePath) else {
+ FileHandle.standardError.write(Data("Failed to load \(sourcePath)\n".utf8))
+ exit(1)
+}
+
+let sizes: [(name: String, pixels: 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)
+]
+
+try FileManager.default.createDirectory(atPath: outputDir, withIntermediateDirectories: true)
+
+for (name, pixels) in sizes {
+ let size = NSSize(width: pixels, height: pixels)
+ let rendered = NSImage(size: size)
+ rendered.lockFocus()
+ NSGraphicsContext.current?.imageInterpolation = .high
+ source.draw(in: NSRect(origin: .zero, size: size), from: .zero, operation: .sourceOver, fraction: 1.0)
+ rendered.unlockFocus()
+
+ guard let tiff = rendered.tiffRepresentation,
+ let rep = NSBitmapImageRep(data: tiff),
+ let png = rep.representation(using: .png, properties: [:]) else {
+ FileHandle.standardError.write(Data("Failed to rasterize \(name)\n".utf8))
+ exit(1)
+ }
+ let url = URL(fileURLWithPath: outputDir).appendingPathComponent("\(name).png")
+ try png.write(to: url)
+}
+
+print("Wrote \(sizes.count) icon sizes to \(outputDir)")