diff --git a/assets/Localizable.xcstrings b/assets/Localizable.xcstrings index 88a0131..aed5d7a 100644 --- a/assets/Localizable.xcstrings +++ b/assets/Localizable.xcstrings @@ -257,6 +257,22 @@ } } }, + "Build Manifest": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Build Manifest" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "构建清单" + } + } + } + }, "Build Release": { "localizations": { "en": { @@ -481,6 +497,38 @@ } } }, + "Copy Bundle ID": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Copy Bundle ID" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "复制包标识符" + } + } + } + }, + "Copy SHA-256": { + "localizations": { + "en": { + "stringUnit": { + "state": "translated", + "value": "Copy SHA-256" + } + }, + "zh-Hans": { + "stringUnit": { + "state": "translated", + "value": "复制 SHA-256" + } + } + } + }, "Couldn’t Open Application": { "localizations": { "en": { diff --git a/src/AppConfiguration.swift b/src/AppConfiguration.swift index 1e8cd10..66f6a8a 100644 --- a/src/AppConfiguration.swift +++ b/src/AppConfiguration.swift @@ -18,6 +18,11 @@ enum AppConfiguration { repository.appending(path: "commit").appending(component: revision) } + static func manifestURL(appID: String, revision: String) -> URL { + buildsRepositoryURL.appending(path: "blob").appending(component: revision) + .appending(path: "apps").appending(component: appID).appending(component: "manifest.yaml") + } + static func assetURL(appID: String, version: String, filename: String) -> URL { buildsRepositoryURL.appending(path: "releases/download") .appending(component: "\(appID)/\(version)").appending(component: filename) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9db1283..d5322ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(SimStore MACOSX_BUNDLE CatalogBrowserView.swift CatalogAppCard.swift CatalogAppDetailView.swift + CopyableText.swift SidebarView.swift NoSimulatorsView.swift MissingXcodeView.swift diff --git a/src/CatalogAppDetailView.swift b/src/CatalogAppDetailView.swift index 1005974..9353afa 100644 --- a/src/CatalogAppDetailView.swift +++ b/src/CatalogAppDetailView.swift @@ -98,8 +98,8 @@ struct CatalogAppDetailView: View { } GridRow { Text("Bundle ID").foregroundStyle(.secondary) - Text(verbatim: app.bundleID).font(.system(size: 11, design: .monospaced)) - .textSelection(.enabled) + CopyableText(value: app.bundleID, copyLabel: "Copy Bundle ID") + .font(.system(size: 11, design: .monospaced)) } } .font(.system(size: 13)) @@ -125,6 +125,13 @@ struct CatalogAppDetailView: View { Link(destination: app.repository) { Label("Source Repository", systemImage: "curlybraces") } + Link( + destination: AppConfiguration.manifestURL( + appID: app.id, revision: version.recipeRevision) + ) { + Label("Build Manifest", systemImage: "doc.text") + } + .accessibilityIdentifier("build-manifest") Link(destination: AppConfiguration.releaseURL(appID: app.id, version: version.version)) { Label("Build Release", systemImage: "arrow.up.right.square") @@ -142,7 +149,7 @@ struct CatalogAppDetailView: View { link: AppConfiguration.commitURL( repository: AppConfiguration.buildsRepositoryURL, revision: version.recipeRevision )) - buildDetail("SHA-256", value: version.sha256) + buildDetail("SHA-256", value: version.sha256, copyLabel: "Copy SHA-256") } .padding(.top, 12) } @@ -188,14 +195,17 @@ struct CatalogAppDetailView: View { .frame(width: 580) } - private func buildDetail(_ title: LocalizedStringKey, value: String, link: URL? = nil) - -> some View - { + private func buildDetail( + _ title: LocalizedStringKey, value: String, link: URL? = nil, + copyLabel: LocalizedStringKey? = nil + ) -> some View { VStack(alignment: .leading, spacing: 4) { Text(title).foregroundStyle(.secondary) Group { if let link { Link(destination: link) { Text(verbatim: value) } + } else if let copyLabel { + CopyableText(value: value, copyLabel: copyLabel) } else { Text(verbatim: value).textSelection(.enabled) } diff --git a/src/CopyableText.swift b/src/CopyableText.swift new file mode 100644 index 0000000..8a1dbe8 --- /dev/null +++ b/src/CopyableText.swift @@ -0,0 +1,24 @@ +import AppKit +import SwiftUI + +struct CopyableText: View { + let value: String + let copyLabel: LocalizedStringKey + + var body: some View { + HStack(alignment: .firstTextBaseline, spacing: 8) { + Text(verbatim: value).textSelection(.enabled) + Button { + let pasteboard = NSPasteboard.general + pasteboard.clearContents() + pasteboard.setString(value, forType: .string) + } label: { + Label(copyLabel, systemImage: "doc.on.doc") + } + .labelStyle(.iconOnly) + .buttonStyle(.borderless) + .controlSize(.small) + .help(Text(copyLabel)) + } + } +}