Last active
March 20, 2025 09:49
-
-
Save cornradio/f7f1414232fdce505008cb21ab9ad613 to your computer and use it in GitHub Desktop.
cool1.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import AppKit | |
struct AppInfo: Identifiable, Codable, Equatable, Hashable { | |
let id = UUID() | |
let name: String | |
let path: String | |
var isFavorite: Bool = false | |
} | |
struct HistoryItemView: View { | |
let app: AppInfo | |
let onLaunch: () -> Void | |
let onToggleFavorite: () -> Void | |
let onDelete: () -> Void | |
let onMove: (UUID, UUID) -> Void | |
@State private var isRunning: Bool = false | |
var body: some View { | |
HStack { | |
Button(action: onLaunch) { | |
Image(systemName: isRunning ? "arrowtriangle.forward.fill" : "arrowtriangle.forward") | |
.foregroundColor(isRunning ? .green : .white) | |
} | |
Image(nsImage: NSWorkspace.shared.icon(forFile: app.path)) | |
.resizable() | |
.frame(width: 20, height: 20) | |
Text(app.name) | |
Spacer() | |
Button(action: onToggleFavorite) { | |
Image(systemName: app.isFavorite ? "star.fill" : "star") | |
.foregroundColor(app.isFavorite ? .yellow : .gray) | |
} | |
Button(action: onDelete) { | |
Image(systemName: "trash.fill") | |
} | |
} | |
.draggable(app.id.uuidString) { Text(app.name) } | |
.dropDestination(for: String.self) { items, location in | |
guard let draggedId = items.first, | |
let draggedUUID = UUID(uuidString: draggedId), | |
draggedId != app.id.uuidString else { | |
return false | |
} | |
onMove(draggedUUID, app.id) | |
return true | |
} isTargeted: { _ in } | |
// .onTapGesture { | |
// onLaunch() | |
// } | |
.onAppear { | |
checkIfAppIsRunning() | |
} | |
.onReceive(Timer.publish(every: 2, on: .main, in: .common).autoconnect()) { _ in | |
checkIfAppIsRunning() | |
} | |
} | |
private func checkIfAppIsRunning() { | |
let bundleURL = URL(fileURLWithPath: app.path) | |
if let bundle = Bundle(url: bundleURL), | |
let bundleIdentifier = bundle.bundleIdentifier { | |
isRunning = NSWorkspace.shared.runningApplications.contains { | |
$0.bundleIdentifier == bundleIdentifier | |
} | |
} | |
} | |
} | |
struct ContentView: View { | |
@State private var apps: [AppInfo] = [] | |
@State private var selectedApp: AppInfo? | |
@State private var history: [AppInfo] = [] | |
private let historyKey = "AppLaunchHistory" | |
var body: some View { | |
HStack { | |
VStack { | |
HStack{ | |
Picker("选择应用", selection: $selectedApp) { | |
ForEach(apps) { app in | |
Text(app.name).tag(app as AppInfo?) | |
} | |
} | |
.pickerStyle(MenuPickerStyle()) | |
Button("启动") { | |
launchSelectedApp() | |
}.disabled(selectedApp == nil) | |
} | |
Divider() | |
HStack{ | |
Text("历史记录") | |
.font(.headline) | |
Spacer() | |
Button(action: deleteAllNonFavoriteApps) { | |
Text("清空未收藏") | |
} | |
.disabled(history.filter { !$0.isFavorite }.isEmpty) | |
} | |
List { | |
ForEach(history) { app in | |
HistoryItemView( | |
app: app, | |
onLaunch: { launchAppFromHistory(app: app) }, | |
onToggleFavorite: { toggleFavorite(app: app) }, | |
onDelete: { deleteAppFromHistory(app: app) }, | |
onMove: { fromId, toId in | |
if let fromIndex = history.firstIndex(where: { $0.id == fromId }), | |
let toIndex = history.firstIndex(where: { $0.id == toId }) { | |
let item = history.remove(at: fromIndex) | |
history.insert(item, at: toIndex) | |
saveHistory() | |
} | |
} | |
) | |
} | |
} | |
} | |
.padding() | |
} | |
.frame(width: 400, height: 500) | |
.onAppear { | |
loadInstalledApps() | |
loadHistory() | |
} | |
} | |
private func loadInstalledApps() { | |
let fileManager = FileManager.default | |
let appsDirectory = "/Applications" | |
do { | |
let appUrls = try fileManager.contentsOfDirectory(atPath: appsDirectory) | |
apps = appUrls.compactMap { appName in | |
let appPath = "\(appsDirectory)/\(appName)" | |
if appName.hasSuffix(".app") { | |
let name = (appName as NSString).deletingPathExtension | |
return AppInfo(name: name, path: appPath) | |
} | |
return nil | |
}.sorted { $0.name < $1.name } | |
} catch { | |
print("Failed to load apps: \(error)") | |
} | |
} | |
private func launchSelectedApp() { | |
guard let app = selectedApp else { return } | |
NSWorkspace.shared.open(URL(fileURLWithPath: app.path)) | |
addToHistory(app: app) | |
} | |
private func launchAppFromHistory(app: AppInfo) { | |
NSWorkspace.shared.open(URL(fileURLWithPath: app.path)) | |
selectedApp = app | |
addToHistory(app: app) | |
} | |
private func addToHistory(app: AppInfo) { | |
if !history.contains(where: { $0.path == app.path }) { | |
history.insert(app, at: 0) | |
} | |
saveHistory() | |
} | |
private func deleteAppFromHistory(app: AppInfo) { | |
if let index = history.firstIndex(where: { $0.id == app.id }) { | |
history.remove(at: index) | |
} | |
saveHistory() | |
} | |
private func toggleFavorite(app: AppInfo) { | |
if let index = history.firstIndex(where: { $0.id == app.id }) { | |
history[index].isFavorite.toggle() | |
} | |
saveHistory() | |
} | |
private func deleteAllNonFavoriteApps() { | |
// Remove all history items that are not marked as favorite | |
history.removeAll { !$0.isFavorite } | |
saveHistory() | |
} | |
private func saveHistory() { | |
if let encoded = try? JSONEncoder().encode(history) { | |
UserDefaults.standard.set(encoded, forKey: historyKey) | |
} | |
} | |
private func loadHistory() { | |
if let savedData = UserDefaults.standard.data(forKey: historyKey), | |
let savedHistory = try? JSONDecoder().decode([AppInfo].self, from: savedData) { | |
history = savedHistory | |
} | |
} | |
// Function to get the app icon from its path | |
private func getAppIcon(for appPath: String) -> NSImage { | |
return NSWorkspace.shared.icon(forFile: appPath) | |
} | |
} |
Author
cornradio
commented
Mar 20, 2025
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment