Fix: stderr-Pipe gegen Instant-Exit-Datenverlust, TCP-Vorab-Check, Fehler-Banner; Icon (Dracula) im Bundle
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env swift
|
||||
// Generiert das RemminaMac App-Icon (Dracula-Terminal) als PNG-Set + .iconset.
|
||||
// Aufruf: swift scripts/RenderIcon.swift <output-dir>
|
||||
// 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)")
|
||||
@@ -18,6 +18,7 @@ rm -rf "$APP"
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
|
||||
cp "$BIN" "$APP/Contents/MacOS/RemminaMac"
|
||||
chmod +x "$APP/Contents/MacOS/RemminaMac"
|
||||
cp "$ROOT/Resources/AppIcon.icns" "$APP/Contents/Resources/" 2>/dev/null || echo "[build-app] Hinweis: kein AppIcon.icns (./scripts/generate-icon.sh)"
|
||||
|
||||
# Info.plist mit Version versehen
|
||||
sed "s|<string>0.1.0</string>|<string>$VERSION</string>|" "$ROOT/Resources/Info.plist" > "$APP/Contents/Info.plist"
|
||||
|
||||
Executable
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
# Generiert AppIcon.icns + Doku-Vorschau aus scripts/RenderIcon.swift (nur Bordmittel).
|
||||
# Usage: ./scripts/generate-icon.sh
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ICONSET="/tmp/RemminaMac.iconset"
|
||||
rm -rf "$ICONSET"
|
||||
swift "$ROOT/scripts/RenderIcon.swift" "$ICONSET"
|
||||
cp "$ICONSET/RemminaMac-preview-tmp.png" "$ROOT/docs/icon-preview.png"
|
||||
iconutil -c icns "$ICONSET" -o "$ROOT/Resources/AppIcon.icns"
|
||||
echo "[icon] Fertig:"
|
||||
ls -lh "$ROOT/Resources/AppIcon.icns" "$ROOT/docs/icon-preview.png"
|
||||
Reference in New Issue
Block a user