Parcourir la source

修复桌面应用图标透明背景,添加纯白圆角底板

Samien il y a 1 mois
Parent
commit
62d08e9bbb
3 fichiers modifiés avec 60 ajouts et 1 suppressions
  1. 1 1
      electron-builder.yml
  2. 55 0
      scripts/build-desktop-icon.swift
  3. 4 0
      scripts/build-desktop.mjs

+ 1 - 1
electron-builder.yml

@@ -35,7 +35,7 @@ mac:
   signIgnore:
     - /web-service/node$
   category: public.app-category.productivity
-  icon: public/icons/icon-512.png
+  icon: .desktop-build/branding/icon.icns
   target:
     - target: dir
       arch: [arm64]

+ 55 - 0
scripts/build-desktop-icon.swift

@@ -0,0 +1,55 @@
+import AppKit
+
+// Keep the shared brand artwork intact; the white tile belongs to the macOS app icon.
+let arguments = CommandLine.arguments
+guard arguments.count == 3,
+      let logo = NSImage(contentsOfFile: arguments[1]) else {
+    fatalError("Usage: swift build-desktop-icon.swift <brand.png> <output-directory>")
+}
+let output = URL(fileURLWithPath: arguments[2], isDirectory: true)
+let iconset = output.appendingPathComponent("icon.iconset", isDirectory: true)
+try FileManager.default.createDirectory(at: iconset, withIntermediateDirectories: true)
+
+func render(size: Int) throws -> Data {
+    let bitmap = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: size, pixelsHigh: size,
+        bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false,
+        colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 0)!
+    let context = NSGraphicsContext(bitmapImageRep: bitmap)!
+    NSGraphicsContext.saveGraphicsState()
+    NSGraphicsContext.current = context
+    context.imageInterpolation = .high
+    context.shouldAntialias = true
+    let scale = CGFloat(size) / 1024
+    context.cgContext.clear(CGRect(x: 0, y: 0, width: size, height: size))
+    context.cgContext.scaleBy(x: scale, y: scale)
+    NSColor.white.setFill()
+    NSBezierPath(roundedRect: NSRect(x: 100, y: 100, width: 824, height: 824),
+        xRadius: 184, yRadius: 184).fill()
+    logo.draw(in: NSRect(x: 140, y: 140, width: 744, height: 744),
+        from: .zero, operation: .sourceOver, fraction: 1)
+    context.flushGraphics()
+    NSGraphicsContext.restoreGraphicsState()
+
+    // These points catch an absent tile or an accidentally flattened outer canvas.
+    for (x, y) in [(0, 0), (size - 1, 0), (0, size - 1), (size - 1, size - 1)] {
+        precondition(bitmap.colorAt(x: x, y: y)!.alphaComponent == 0,
+            "The icon's outer corners must be transparent")
+    }
+    let center = bitmap.colorAt(x: size / 2, y: size / 2)!.usingColorSpace(.deviceRGB)!
+    precondition(center.alphaComponent == 1 && center.redComponent == 1 &&
+        center.greenComponent == 1 && center.blueComponent == 1,
+        "The icon's interior must have an opaque, pure-white background")
+    return bitmap.representation(using: .png, properties: [:])!
+}
+
+for points in [16, 32, 128, 256, 512] {
+    for scale in [1, 2] {
+        let suffix = scale == 2 ? "@2x" : ""
+        let data = try render(size: points * scale)
+        try data.write(to: iconset.appendingPathComponent("icon_\(points)x\(points)\(suffix).png"))
+        if points == 512 && scale == 2 {
+            try data.write(to: output.appendingPathComponent("icon.png"))
+        }
+    }
+}
+print("桌面图标已生成:纯白底板,10 个尺寸,透明度校验通过")

+ 4 - 0
scripts/build-desktop.mjs

@@ -8,6 +8,10 @@ import { sealRuntime } from '../bin/lib/runtime-release.mjs'
 const root = resolve(import.meta.dirname, '..'), out = resolve(root, '.desktop-build')
 await rm(out, { recursive: true, force: true })
 await mkdir(out, { recursive: true })
+execFileSync('xcrun', ['swift', resolve(root, 'scripts/build-desktop-icon.swift'),
+  resolve(root, 'public/brand/AppIcon-1024.png'), resolve(out, 'branding')], { stdio: 'inherit' })
+execFileSync('iconutil', ['-c', 'icns', resolve(out, 'branding/icon.iconset'),
+  '-o', resolve(out, 'branding/icon.icns')], { stdio: 'inherit' })
 await build({ entryPoints: [resolve(root, 'src/server/index.ts')], outfile: resolve(out, 'server.mjs'),
   bundle: true, platform: 'node', format: 'esm', target: 'node24', external: ['vite'],
   banner: { js: "import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);" },