macOS: Make version in about dialog clickable (#12007)

- Fixes: https://github.com/ghostty-org/ghostty/issues/11964

Made a private enum type `VersionConfig` to reference whether the
release is a semver or tip, makes it easier for later in the view to
`switch` between cases.

I do think there could be a better place for this enum or we can get rid
of it, open to opinions. Right now version parsing is kind of duplicated
between `AboutView` and `UpdateModalView` so we can also extract to a
common helper if wanted.

Tested by manually setting `Marketing Version` in build settings to 

`1.3.1`
<img width="412" height="532" alt="Screenshot 2026-03-30 at 18 31 15"
src="https://github.com/user-attachments/assets/285bb94d-138b-4169-bb66-684eb04b6ca3"
/>

`332b2aefc`
<img width="412" height="532" alt="Screenshot 2026-03-30 at 18 32 48"
src="https://github.com/user-attachments/assets/fea30d39-bea7-4885-8221-1696e148f45e"
/>

### AI Disclosure
I used Sonnet 4.6 to understand where the version strings came from and
in what format, it read release yml files to see what's going on. Then
it proposed really bad code so I manually went in and cleaned up the
view.
This commit is contained in:
Mitchell Hashimoto
2026-03-31 06:38:59 -07:00
committed by GitHub

View File

@@ -10,6 +10,39 @@ struct AboutView: View {
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 enum VersionConfig {
case stable(version: String)
case tip(commit: String?)
case other(String)
case none
init(version: String?) {
guard let version else { self = .none; return }
if version.range(of: #"^\d+\.\d+\.\d+$"#, options: .regularExpression) != nil {
self = .stable(version: version)
return
}
if version.range(of: #"^[0-9a-f]{7,40}$"#, options: .regularExpression) != nil {
self = .tip(commit: version)
return
}
self = .other(version)
}
var url: URL? {
switch self {
case .stable(let version):
let slug = version.replacingOccurrences(of: ".", with: "-")
return URL(string: "https://ghostty.org/docs/install/release-notes/\(slug)")
default:
return nil
}
}
}
private var versionConfig: VersionConfig { VersionConfig(version: version) }
private var copyright: String? { Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String }
#if os(macOS)
@@ -60,8 +93,15 @@ struct AboutView: View {
.textSelection(.enabled)
VStack(spacing: 2) {
if let version {
PropertyRow(label: "Version", text: version)
switch versionConfig {
case .stable(let version):
PropertyRow(label: "Version", text: version, url: versionConfig.url)
case .tip:
PropertyRow(label: "Version", text: "Tip Release")
case .other(let v):
PropertyRow(label: "Version", text: v)
case .none:
EmptyView()
}
if let build {
PropertyRow(label: "Build", text: build)