Download Sources/PromptGarageApp/Views/RootView.swift from smlflg/PromtGarageMac: direct link, hf CLI and curl.
- Browser
- Download file 5.89 kB
-
https://huggingface.co/smlflg/PromtGarageMac/resolve/main/Sources/PromptGarageApp/Views/RootView.swift
- Command line
-
hf download hf://smlflg/PromtGarageMac/Sources/PromptGarageApp/Views/RootView.swift
-
curl -L -o RootView.swift https://huggingface.co/smlflg/PromtGarageMac/resolve/main/Sources/PromptGarageApp/Views/RootView.swift
5.89 kB
| import SwiftUI | |
| struct RootView: View { | |
| 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 { | |
| 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 { | |
| var model: AppModel | |
| var body: some View { | |
| List(selection: Binding<SidebarDestination?>( | |
| 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) | |
| } | |
| } | |