Feature-komplett für TestFlight. App-Icon-Platzhalter, Siri-Shortcut, Share-Extension, Release-Checklist mit allen externen Apple-Schritten. - scripts/make-appicon.swift: CoreGraphics-basierter Generator für 1024×1024 forest-green PNG mit "C"-Letter und Karten-Stack-Schatten - Asset-Catalog auf Single-Size-AppIcon-Pattern umgestellt - StudyCardsIntent + CardsAppShortcuts (App Intents): Siri- Shortcut "Karten lernen mit Cards" / "Mit Cards lernen" - CardsShareExtension Target: ShareViewController (UIKit-Bootstrap + SwiftUI-Hosting), ShareEditorView mit Text-Edit - PendingShare + PendingShareStore shared in App-Group group.ev.mana.cards - DeckListView zeigt PendingShare-Banner; Tap navigiert zu PendingShareConsumeView mit Deck-Picker + Front/Back-Felder, Submit → POST /cards, danach store.remove - Info.plist: NSPhotoLibraryUsageDescription für Image-Occlusion- Picker, NSUserActivityTypes für Universal-Links - docs/RELEASE_CHECKLIST.md mit externen Schritten: Apple-Developer- Portal, App-IDs, App-Group, AASA, Xcode-Archive, TestFlight-Plan, App-Store-Connect-Felder, Compliance-Verifikation - UI-Test robuster (akzeptiert Login oder Decks/Entdecken als Launch-Erfolg, unabhängig vom Simulator-Keychain-State) - 35 Tests + 1 UI-Test grün, alle drei Targets bauen App-Store-Submission selbst ist externe Aktion und passiert nicht durch dieses Repo — Schritte in docs/RELEASE_CHECKLIST.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
82 lines
2.8 KiB
Swift
82 lines
2.8 KiB
Swift
#!/usr/bin/env swift
|
||
// Generiert ein 1024×1024-AppIcon-PNG als Platzhalter.
|
||
// forest-green Hintergrund + großes weißes "C" mit Karten-Stack-Andeutung.
|
||
//
|
||
// Aufruf:
|
||
// swift scripts/make-appicon.swift
|
||
//
|
||
// Schreibt nach: Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png
|
||
//
|
||
// Dies ist ein Platzhalter — vor App-Store-Submission durch ein
|
||
// professionelles Icon ersetzen (siehe docs/RELEASE_CHECKLIST.md).
|
||
|
||
import AppKit
|
||
import CoreGraphics
|
||
|
||
let size = 1024
|
||
let scale: CGFloat = 1.0
|
||
let cs = CGColorSpaceCreateDeviceRGB()
|
||
guard let ctx = CGContext(
|
||
data: nil,
|
||
width: size, height: size,
|
||
bitsPerComponent: 8, bytesPerRow: 0,
|
||
space: cs,
|
||
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue
|
||
) else {
|
||
print("CGContext creation failed")
|
||
exit(1)
|
||
}
|
||
|
||
// HSL(142, 76%, 28%) — forest primary light. Hand-konvertiert zu sRGB.
|
||
let background = CGColor(red: 0.043, green: 0.494, blue: 0.227, alpha: 1)
|
||
ctx.setFillColor(background)
|
||
ctx.fill(CGRect(x: 0, y: 0, width: size, height: size))
|
||
|
||
// Subtile Karten-Stack-Schatten hinter dem C
|
||
let shadow1 = CGColor(red: 1, green: 1, blue: 1, alpha: 0.08)
|
||
let shadow2 = CGColor(red: 1, green: 1, blue: 1, alpha: 0.05)
|
||
let cardW = CGFloat(size) * 0.62
|
||
let cardH = CGFloat(size) * 0.82
|
||
let cardX = (CGFloat(size) - cardW) / 2
|
||
let cardY = (CGFloat(size) - cardH) / 2
|
||
|
||
ctx.setFillColor(shadow2)
|
||
ctx.beginPath()
|
||
ctx.addPath(CGPath(roundedRect: CGRect(x: cardX + 30, y: cardY - 30, width: cardW, height: cardH),
|
||
cornerWidth: 48, cornerHeight: 48, transform: nil))
|
||
ctx.fillPath()
|
||
|
||
ctx.setFillColor(shadow1)
|
||
ctx.beginPath()
|
||
ctx.addPath(CGPath(roundedRect: CGRect(x: cardX + 15, y: cardY - 15, width: cardW, height: cardH),
|
||
cornerWidth: 48, cornerHeight: 48, transform: nil))
|
||
ctx.fillPath()
|
||
|
||
// Großer weißer "C"-Buchstabe — Helvetica-Neue-Bold
|
||
let attrs: [NSAttributedString.Key: Any] = [
|
||
.font: NSFont(name: "HelveticaNeue-Bold", size: 720) ?? NSFont.boldSystemFont(ofSize: 720),
|
||
.foregroundColor: NSColor.white,
|
||
]
|
||
let str = NSAttributedString(string: "C", attributes: attrs)
|
||
let line = CTLineCreateWithAttributedString(str)
|
||
let bounds = CTLineGetImageBounds(line, ctx)
|
||
let tx = (CGFloat(size) - bounds.width) / 2 - bounds.origin.x
|
||
let ty = (CGFloat(size) - bounds.height) / 2 - bounds.origin.y
|
||
ctx.textPosition = CGPoint(x: tx, y: ty)
|
||
CTLineDraw(line, ctx)
|
||
|
||
// PNG schreiben
|
||
guard let cgImage = ctx.makeImage() else {
|
||
print("makeImage failed")
|
||
exit(1)
|
||
}
|
||
let bitmap = NSBitmapImageRep(cgImage: cgImage)
|
||
guard let data = bitmap.representation(using: .png, properties: [:]) else {
|
||
print("PNG encoding failed")
|
||
exit(1)
|
||
}
|
||
|
||
let target = URL(fileURLWithPath: "Sources/Resources/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png")
|
||
try data.write(to: target)
|
||
print("Wrote \(target.path) (\(data.count) bytes)")
|
||
_ = scale // suppress unused warning
|