spb/zyquo-atlas Public License
The AI-native macOS web browser — every surface, intelligent.
Swift 75.2%
JavaScript 22%
Shell 2%
Makefile 0.9%
1//2// DownloadsPopover.swift3// Zyquo Atlas4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// Toolbar downloads popover: in-flight and finished downloads with progress,9// reveal-in-Finder, and open. Backed by the window's DownloadManager.10//1112import SwiftUI1314struct DownloadsPopover: View {15 @ObservedObject var downloads: DownloadManager16 let onClose: () -> Void17 @EnvironmentObject private var themeEngine: ThemeEngine18 private var theme: AtlasTheme { themeEngine.theme }1920 var body: some View {21 VStack(alignment: .leading, spacing: 0) {22 HStack {23 Text("Downloads").font(ZyquoFont.bodyEmphasis()).foregroundStyle(theme.textPrimary)24 Spacer()25 if downloads.items.contains(where: { $0.state == .finished }) {26 Button("Clear") { downloads.clearFinished() }27 .font(ZyquoFont.caption).foregroundStyle(theme.accent)28 }29 Button { onClose() } label: { Image(systemName: "xmark") }30 .buttonStyle(.plain).foregroundStyle(theme.textTertiary)31 }32 .padding(ZyquoSpacing.sm)33 Divider().overlay(theme.border)3435 if downloads.items.isEmpty {36 Text("No downloads yet")37 .font(ZyquoFont.caption).foregroundStyle(theme.textTertiary)38 .frame(maxWidth: .infinity).padding(ZyquoSpacing.lg)39 } else {40 ScrollView {41 VStack(spacing: 0) {42 ForEach(downloads.items) { item in43 row(item)44 Divider().overlay(theme.border)45 }46 }47 }48 .frame(maxHeight: 320)49 }50 }51 .frame(width: 320)52 .background(RoundedRectangle(cornerRadius: ZyquoRadius.large).fill(theme.surface))53 .overlay(RoundedRectangle(cornerRadius: ZyquoRadius.large).strokeBorder(theme.border, lineWidth: ZyquoMetrics.hairline))54 .shadow(color: ZyquoShadow.soft.color, radius: ZyquoShadow.soft.radius, y: ZyquoShadow.soft.y)55 }5657 private func row(_ item: DownloadItem) -> some View {58 HStack(spacing: ZyquoSpacing.sm) {59 Image(systemName: icon(item)).foregroundStyle(color(item))60 VStack(alignment: .leading, spacing: 2) {61 Text(item.filename).font(ZyquoFont.body(size: 12)).lineLimit(1)62 .foregroundStyle(theme.textPrimary)63 if item.state == .inProgress {64 ProgressView(value: item.fraction).controlSize(.small).tint(theme.accent)65 } else {66 Text(item.state == .finished ? "Completed" : "Failed")67 .font(ZyquoFont.caption).foregroundStyle(theme.textTertiary)68 }69 }70 Spacer()71 if item.state == .finished {72 Button { downloads.open(item) } label: { Image(systemName: "arrow.up.forward.app") }73 .buttonStyle(.plain).foregroundStyle(theme.textSecondary)74 Button { downloads.reveal(item) } label: { Image(systemName: "magnifyingglass") }75 .buttonStyle(.plain).foregroundStyle(theme.textSecondary)76 }77 }78 .padding(ZyquoSpacing.sm)79 }8081 private func icon(_ i: DownloadItem) -> String {82 switch i.state { case .inProgress: return "arrow.down.circle"83 case .finished: return "checkmark.circle.fill"; case .failed: return "exclamationmark.circle.fill" }84 }85 private func color(_ i: DownloadItem) -> Color {86 switch i.state { case .inProgress: return theme.accent87 case .finished: return theme.success; case .failed: return theme.danger }88 }89}90