import SwiftUI struct RootView: View { @Bindable var model: AppModel var body: some View { ZStack { Group { switch model.launchState { case .checking, .needsDatabase, .validating, .invalid: FirstLaunchView(model: model) case .ready, .readOnly: AppShellView(model: model) case .failed: FirstLaunchView(model: model) } } if let message = model.successMessage { VStack { Spacer() SuccessToast(message: message) .padding(.bottom, 42) } .transition(.move(edge: .bottom).combined(with: .opacity)) .allowsHitTesting(false) } } .tint(AppUI.accent) .animation(.snappy(duration: 0.25), value: model.successMessage) .sheet(item: $model.editor) { presentation in PromptEditorSheet(model: model, presentation: presentation) } .alert( "Prompt löschen?", isPresented: Binding( get: { model.promptPendingDeletion != nil }, set: { if !$0 { model.promptPendingDeletion = nil } } ), presenting: model.promptPendingDeletion ) { _ in Button("Abbrechen", role: .cancel) { model.promptPendingDeletion = nil } Button("Endgültig löschen", role: .destructive) { Task { await model.confirmDelete() } } } message: { prompt in Text("„\(prompt.title)“ wird aus der lokalen Datenbank entfernt. Das kann nicht rückgängig gemacht werden.") } } } private struct AppShellView: View { @Bindable var model: AppModel var body: some View { VStack(spacing: 0) { if let error = model.errorPresentation { ErrorBanner( error: error, dismiss: model.dismissError, copyDiagnostic: model.copyErrorDiagnostic ) } NavigationSplitView { SidebarView(model: model) .navigationSplitViewColumnWidth(min: 185, ideal: 215, max: 250) } content: { switch model.destination { case .all, .favorites, .mostUsed, .byDay, .byTopic: LibraryView(model: model) case .importCenter: ImportCenterView(model: model) case .statistics: StatisticsView(model: model) } } detail: { switch model.destination { case .all, .favorites, .mostUsed, .byDay, .byTopic: PromptDetailView(model: model) case .importCenter: ImportPreviewDetailView(model: model) case .statistics: StatisticsDetailView(model: model) } } .navigationSplitViewStyle(.balanced) HStack { LocalStatusLabel(databaseName: model.databaseDisplayName) Spacer() if model.isReadOnly { StatusChip(title: "Nur Lesen", systemImage: "lock.fill", tint: .red) } Text(model.footerStatus) .font(.caption) .foregroundStyle(.tertiary) } .padding(.horizontal, 12) .frame(height: 28) .background(.bar) .overlay(alignment: .top) { Divider() } } } } private struct SidebarView: View { @Bindable var model: AppModel var body: some View { List(selection: Binding( get: { model.destination }, set: { destination in if let destination { model.destination = destination } } )) { Section("Bibliothek") { sidebarItem(.all, title: "Alle Prompts", icon: "tray.full") sidebarItem(.favorites, title: "Favoriten", icon: "star") sidebarItem(.mostUsed, title: "Meistgenutzt", icon: "arrow.up.forward.circle") sidebarItem(.byDay, title: "Nach Tagen", icon: "calendar") sidebarItem(.byTopic, title: "Nach Topics", icon: "tag") } Section("Werkzeuge") { sidebarItem(.importCenter, title: "Import", icon: "square.and.arrow.down") sidebarItem(.statistics, title: "Statistik", icon: "chart.bar.xaxis") } } .listStyle(.sidebar) .navigationTitle("PromptGarage") .safeAreaInset(edge: .bottom) { VStack(alignment: .leading, spacing: 8) { Divider() HStack(spacing: 9) { Image(systemName: "externaldrive.fill") .foregroundStyle(AppUI.accent) VStack(alignment: .leading, spacing: 1) { Text("Diese Garage") .font(.caption.weight(.semibold)) Text(model.databaseDisplayName ?? "Lokale Datenbank") .font(.caption2) .foregroundStyle(.secondary) .lineLimit(1) } } .padding(.horizontal, 10) .padding(.bottom, 8) } .background(.bar) } } private func sidebarItem( _ destination: SidebarDestination, title: String, icon: String ) -> some View { Label(title, systemImage: icon) .tag(destination) } }