Add Tonuino Manager website and Sparkle updates

This commit is contained in:
maddin committed 2026-10-31 16:40:48 +01:00
commit 1cac427ff2
44 files changed
+5861

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
import AppKit
import Combine
import SwiftUI
@MainActor
final class AppModel: ObservableObject {
enum Section: String, CaseIterable, Identifiable, Hashable {
case library = "SD-Karte"
case cards = "NFC-Karten"
case tonuino = "TonUINO"
case settings = "Einstellungen"
var id: String { rawValue }
var icon: String {
switch self {
case .library: "sdcard"
case .cards: "wave.3.right"
case .tonuino: "slider.horizontal.3"
case .settings: "gearshape"
}
}
}
@Published var section: Section = .library
@Published var library = LibraryModel()
@Published var cards = CardModel()
@Published var status = "Bereit"
@Published var errorMessage: String?
@Published var showError = false
private var childSubscriptions = Set<AnyCancellable>()
init() {
library.objectWillChange.sink { [weak self] _ in self?.objectWillChange.send() }.store(in: &childSubscriptions)
cards.objectWillChange.sink { [weak self] _ in self?.objectWillChange.send() }.store(in: &childSubscriptions)
}
func report(_ error: Error) {
errorMessage = error.localizedDescription
showError = true
}
}
@@ -0,0 +1,53 @@
import SwiftUI
struct ContentView: View {
@EnvironmentObject private var app: AppModel
var body: some View {
VStack(spacing: 0) {
NavigationSplitView {
List(AppModel.Section.allCases, selection: $app.section) { item in
Label(item.rawValue, systemImage: item.icon).tag(item)
}
.listStyle(.sidebar)
.navigationTitle("Tonuino Manager")
.safeAreaInset(edge: .bottom) {
HStack(spacing: 8) {
Circle().fill(app.library.rootURL == nil ? Color.secondary : Color.green).frame(width: 8, height: 8)
Text(app.library.activeProfile.map { "Lokal · \($0.name)" } ?? (app.library.rootURL?.lastPathComponent ?? "Keine Bibliothek ausgewählt"))
.lineLimit(1).foregroundStyle(.secondary)
}
.font(.caption).padding()
}
} detail: {
switch app.section {
case .library: LibraryView()
case .cards: CardsView()
case .tonuino: TonuinoSettingsView()
case .settings: SettingsView()
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
HStack {
Spacer(minLength: 0)
Text(app.status)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
.truncationMode(.tail)
.frame(maxWidth: 520, alignment: .trailing)
}
.padding(.horizontal, 14)
.padding(.vertical, 6)
.frame(maxWidth: .infinity)
.background(.regularMaterial)
.overlay(alignment: .top) {
Rectangle().fill(.primary.opacity(0.08)).frame(height: 1)
}
}
.alert("Fehler", isPresented: $app.showError) {
Button("OK", role: .cancel) { }
} message: { Text(app.errorMessage ?? "Unbekannter Fehler") }
}
}
@@ -0,0 +1,35 @@
import SwiftUI
import Sparkle
import AppKit
final class AppDelegate: NSObject, NSApplicationDelegate {
static var updater: SPUStandardUpdaterController?
func applicationDidFinishLaunching(_ notification: Notification) {
guard let feed = Bundle.main.object(forInfoDictionaryKey: "SUFeedURL") as? String,
URL(string: feed)?.scheme != nil,
let key = Bundle.main.object(forInfoDictionaryKey: "SUPublicEDKey") as? String,
!key.isEmpty else { return }
Self.updater = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
}
}
@main
struct TonuinoManagerApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
@StateObject private var model = AppModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(model)
.frame(minWidth: 980, minHeight: 640)
}
.commands {
CommandGroup(replacing: .newItem) { }
CommandGroup(after: .appInfo) {
Button("Auf Updates prüfen…") { AppDelegate.updater?.checkForUpdates(nil) }
.disabled(AppDelegate.updater?.updater.canCheckForUpdates != true)
}
}
}
}