vibetunnel/mac/VibeTunnel/Presentation/Views/AboutView.swift
2025-06-22 11:53:00 +02:00

153 lines
4.6 KiB
Swift

import AppKit
import SwiftUI
/// About view displaying application information, version details, and credits.
///
/// This view provides information about VibeTunnel including version numbers,
/// build details, developer credits, and links to external resources like
/// GitHub repository and support channels.
struct AboutView: View {
var appName: String {
Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ??
Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "VibeTunnel"
}
var appVersion: String {
let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "Unknown"
return "\(version) (\(build))"
}
var body: some View {
ScrollView {
VStack(spacing: 24) {
appInfoSection
descriptionSection
linksSection
Spacer(minLength: 10)
copyrightSection
}
.frame(maxWidth: .infinity)
.standardPadding()
}
.scrollContentBackground(.hidden)
}
private var appInfoSection: some View {
VStack(spacing: 16) {
GlowingAppIcon(
size: 128,
enableFloating: true,
enableInteraction: true,
glowIntensity: 0.3,
action: openWebsite
)
.padding(.bottom, 20)
Text(appName)
.font(.largeTitle)
.fontWeight(.medium)
Text("Version \(appVersion)")
.font(.footnote)
.foregroundStyle(.secondary)
}
.padding(.top, 40)
}
@MainActor
private func openWebsite() {
guard let url = URL(string: "https://vibetunnel.sh") else { return }
NSWorkspace.shared.open(url)
}
private var descriptionSection: some View {
Text("Turn any browser into your terminal & command your agents on the go.")
.font(.body)
.foregroundStyle(.secondary)
}
private var linksSection: some View {
VStack(spacing: 12) {
HoverableLink(url: "https://vibetunnel.sh", title: "Website", icon: "globe")
HoverableLink(url: "https://github.com/amantus-ai/vibetunnel", title: "View on GitHub", icon: "link")
HoverableLink(
url: "https://github.com/amantus-ai/vibetunnel/issues",
title: "Report an Issue",
icon: "exclamationmark.bubble"
)
HoverableLink(url: "https://x.com/VibeTunnel", title: "Follow @VibeTunnel", icon: "bird")
}
}
private var copyrightSection: some View {
VStack(spacing: 8) {
// Credits
VStack(spacing: 4) {
Text("Brought to you by")
.font(.caption)
.foregroundColor(.secondary)
HStack(spacing: 4) {
CreditLink(name: "@badlogic", url: "https://mariozechner.at/")
Text("")
.font(.caption)
.foregroundColor(.secondary)
CreditLink(name: "@mitsuhiko", url: "https://lucumr.pocoo.org/")
Text("")
.font(.caption)
.foregroundColor(.secondary)
CreditLink(name: "@steipete", url: "https://steipete.me")
}
}
Text("© 2025 • MIT Licensed")
.font(.footnote)
.foregroundStyle(.secondary)
}
.padding(.bottom, 32)
}
}
/// Hoverable link component with underline animation.
///
/// This component displays a link with an icon that shows an underline on hover
/// and changes the cursor to a pointing hand for better user experience.
struct HoverableLink: View {
let url: String
let title: String
let icon: String
@State private var isHovering = false
private var destinationURL: URL {
URL(string: url) ?? URL(fileURLWithPath: "/")
}
var body: some View {
Link(destination: destinationURL) {
Label(title, systemImage: icon)
.underline(isHovering, color: .accentColor)
}
.buttonStyle(.link)
.pointingHandCursor()
.onHover { hovering in
withAnimation(.easeInOut(duration: 0.2)) {
isHovering = hovering
}
}
}
}
// MARK: - Preview
#Preview("About View") {
AboutView()
.frame(width: 570, height: 600)
}