Files
ghostty/macos/Sources/Features/About/AboutView.swift
Jon Parise 1537590a5f macos: cycle through our icons in the About view
Clicking on the icon immediately advances to the next one. Hovering on
the icon pauses the automatic cycling, and the "help" tooltip displays
the icon's configuration name (for `macos-icon`).
2026-01-12 15:20:14 -05:00

155 lines
5.2 KiB
Swift

import SwiftUI
struct AboutView: View {
@Environment(\.openURL) var openURL
private let githubURL = URL(string: "https://github.com/ghostty-org/ghostty")
private let docsURL = URL(string: "https://ghostty.org/docs")
/// Read the commit from the bundle.
private var build: String? { Bundle.main.infoDictionary?["CFBundleVersion"] as? String }
private var commit: String? { Bundle.main.infoDictionary?["GhosttyCommit"] as? String }
private var version: String? { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String }
private var copyright: String? { Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String }
#if os(macOS)
// This creates a background style similar to the Apple "About My Mac" Window
private struct VisualEffectBackground: NSViewRepresentable {
let material: NSVisualEffectView.Material
let blendingMode: NSVisualEffectView.BlendingMode
let isEmphasized: Bool
init(material: NSVisualEffectView.Material,
blendingMode: NSVisualEffectView.BlendingMode = .behindWindow,
isEmphasized: Bool = false)
{
self.material = material
self.blendingMode = blendingMode
self.isEmphasized = isEmphasized
}
func updateNSView(_ nsView: NSVisualEffectView, context: Context) {
nsView.material = material
nsView.blendingMode = blendingMode
nsView.isEmphasized = isEmphasized
}
func makeNSView(context: Context) -> NSVisualEffectView {
let visualEffect = NSVisualEffectView()
visualEffect.autoresizingMask = [.width, .height]
return visualEffect
}
}
#endif
var body: some View {
VStack(alignment: .center) {
CyclingIconView()
VStack(alignment: .center, spacing: 32) {
VStack(alignment: .center, spacing: 8) {
Text("Ghostty")
.bold()
.font(.title)
Text("Fast, native, feature-rich terminal \nemulator pushing modern features.")
.multilineTextAlignment(.center)
.fixedSize(horizontal: false, vertical: true)
.font(.caption)
.tint(.secondary)
.opacity(0.8)
}
.textSelection(.enabled)
VStack(spacing: 2) {
if let version {
PropertyRow(label: "Version", text: version)
}
if let build {
PropertyRow(label: "Build", text: build)
}
if let commit, commit != "",
let url = githubURL?.appendingPathComponent("/commits/\(commit)") {
PropertyRow(label: "Commit", text: commit, url: url)
}
}
.frame(maxWidth: .infinity)
HStack(spacing: 8) {
if let url = docsURL {
Button("Docs") {
openURL(url)
}
}
if let url = githubURL {
Button("GitHub") {
openURL(url)
}
}
}
if let copy = self.copyright {
Text(copy)
.font(.caption)
.textSelection(.enabled)
.tint(.secondary)
.opacity(0.8)
.multilineTextAlignment(.center)
.frame(maxWidth: .infinity)
}
}
.frame(maxWidth: .infinity)
}
.padding(.top, 8)
.padding(32)
.frame(minWidth: 256)
#if os(macOS)
.background(VisualEffectBackground(material: .underWindowBackground).ignoresSafeArea())
#endif
}
private struct PropertyRow: View {
private let label: String
private let text: String
private let url: URL?
init(label: String, text: String, url: URL? = nil) {
self.label = label
self.text = text
self.url = url
}
@ViewBuilder private var textView: some View {
Text(text)
.frame(width: 125, alignment: .leading)
.padding(.leading, 2)
.tint(.secondary)
.opacity(0.8)
.monospaced()
}
var body: some View {
HStack(spacing: 4) {
Text(label)
.frame(width: 126, alignment: .trailing)
.padding(.trailing, 2)
if let url {
Link(destination: url) {
textView
}
} else {
textView
}
}
.font(.callout)
.textSelection(.enabled)
.frame(maxWidth: .infinity)
}
}
}
struct AboutView_Previews: PreviewProvider {
static var previews: some View {
AboutView()
}
}