mirror of
https://github.com/nicoverbruggen/phpmon.git
synced 2026-03-27 22:40:08 +01:00
♻️ Refactor "Open With" with third-party apps
- Icons are now loaded if possible (if the path could be inferred)
- Some new apps that are detected by default:
- Browsers: Safari, Google Chrome, Microsoft Edge, Firefox, Brave,
Arc, Zen
- Editors: WebStorm, VSCodium
- Git GUI: Tower, SourceTree
- Terminal: Ghostty
- `openWithEditor` has been refactored to `openWithApp` which now lets
browsers open domains from their URL, not from their local folder.
This commit is contained in:
@@ -84,8 +84,8 @@ class ExtensionMenuItem: NSMenuItem {
|
||||
var phpExtension: PhpExtension?
|
||||
}
|
||||
|
||||
class EditorMenuItem: NSMenuItem {
|
||||
var editor: Application?
|
||||
class ApplicationMenuItem: NSMenuItem {
|
||||
var app: Application?
|
||||
}
|
||||
|
||||
class PresetMenuItem: NSMenuItem {
|
||||
|
||||
@@ -14,7 +14,7 @@ import Foundation
|
||||
class Application {
|
||||
|
||||
enum AppType {
|
||||
case editor, browser, git_gui, terminal, user_supplied
|
||||
case editor, ide, browser, git_gui, terminal, user_supplied
|
||||
}
|
||||
|
||||
// MARK: - Container
|
||||
@@ -29,24 +29,50 @@ class Application {
|
||||
/// Application type. Depending on the type, a different action might occur.
|
||||
let type: AppType
|
||||
|
||||
/// The full path to the application bundle (if found)
|
||||
var path: String?
|
||||
|
||||
/// Initializer. Used to detect a specific app of a specific type.
|
||||
init(_ container: Container, _ name: String, _ type: AppType) {
|
||||
self.container = container
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.path = determinePath()
|
||||
}
|
||||
|
||||
/**
|
||||
Attempt to open a specific directory in the app of choice.
|
||||
Attempt to open a specific string (path or URL) in the app of choice.
|
||||
(This will open the app if it isn't open yet.)
|
||||
*/
|
||||
@objc public func openDirectory(file: String) {
|
||||
Task { await container.shell.quiet("/usr/bin/open -a \"\(name)\" \"\(file)\"") }
|
||||
@objc public func open(arg: String) {
|
||||
Task { await container.shell.quiet("/usr/bin/open -a \"\(name)\" \"\(arg)\"") }
|
||||
}
|
||||
|
||||
/** Checks if the app is installed. */
|
||||
func isInstalled() async -> Bool {
|
||||
/**
|
||||
Attempt to see if we can locate the app bundle in one of the two default locations:
|
||||
- - First in `/Applications` (system-wide installed apps)
|
||||
- - Second in `~/Applications` (user-specific installed apps)
|
||||
|
||||
If not in one of these default locations, the path will be `nil` and certain operations
|
||||
will not be possible (i.e. determining icon via path to application).
|
||||
*/
|
||||
func determinePath() -> String? {
|
||||
// Check global applications
|
||||
if container.filesystem.directoryExists("/Applications/\(name).app") {
|
||||
return "/Applications/\(name).app"
|
||||
}
|
||||
|
||||
// Check user applications
|
||||
if container.filesystem.directoryExists("~/Applications/\(name).app") {
|
||||
return "~/Applications/\(name).app".replacingTildeWithHomeDirectory
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/** Checks if the app is installed and stores its path. */
|
||||
func isInstalled() async -> Bool {
|
||||
// Then verify it's actually installed using the shell command
|
||||
let (process, output) = try! await container.shell.attach(
|
||||
"/usr/bin/open -Ra \"\(name)\"",
|
||||
didReceiveOutput: { _, _ in },
|
||||
@@ -71,11 +97,30 @@ class Application {
|
||||
var detected: [Application] = []
|
||||
|
||||
let detectable = [
|
||||
Application(container, "PhpStorm", .editor),
|
||||
// Browsers (for future Open In > Browser context menu)
|
||||
Application(container, "Safari", .browser),
|
||||
Application(container, "Google Chrome", .browser),
|
||||
Application(container, "Microsoft Edge", .browser),
|
||||
Application(container, "Firefox", .browser),
|
||||
Application(container, "Brave", .browser),
|
||||
Application(container, "Arc", .browser),
|
||||
Application(container, "Zen", .browser),
|
||||
|
||||
// Editors
|
||||
Application(container, "PhpStorm", .ide),
|
||||
Application(container, "WebStorm", .ide),
|
||||
Application(container, "Visual Studio Code", .editor),
|
||||
Application(container, "VSCodium", .editor),
|
||||
Application(container, "Sublime Text", .editor),
|
||||
|
||||
// Git
|
||||
Application(container, "Sublime Merge", .git_gui),
|
||||
Application(container, "iTerm", .terminal)
|
||||
Application(container, "Tower", .git_gui),
|
||||
Application(container, "SourceTree", .git_gui),
|
||||
|
||||
// Terminals
|
||||
Application(container, "iTerm", .terminal),
|
||||
Application(container, "Ghostty", .terminal)
|
||||
]
|
||||
|
||||
for app in detectable where await app.isInstalled() {
|
||||
|
||||
@@ -38,9 +38,18 @@ extension DomainListVC {
|
||||
Task { await App.shared.container.shell.quiet("open -b com.apple.terminal '\(selectedSite!.absolutePath)'") }
|
||||
}
|
||||
|
||||
@objc func openWithEditor(sender: EditorMenuItem) {
|
||||
guard let editor = sender.editor else { return }
|
||||
editor.openDirectory(file: selectedSite!.absolutePath)
|
||||
@objc func openWithApp(sender: ApplicationMenuItem) {
|
||||
guard let site = selectedSite else { return }
|
||||
guard let app = sender.app else { return }
|
||||
|
||||
if app.type == .browser {
|
||||
guard let url = site.getListableUrl() else { return }
|
||||
// Open the URL for the domain
|
||||
app.open(arg: url.absoluteString)
|
||||
} else {
|
||||
// Open the directory for the domain
|
||||
app.open(arg: site.absolutePath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - UI interaction
|
||||
|
||||
@@ -98,15 +98,22 @@ extension DomainListVC {
|
||||
menu.addItem(NSMenuItem.separator())
|
||||
menu.addItem(HeaderView.asMenuItem(text: "domain_list.detected_apps".localized))
|
||||
|
||||
for editor in applications {
|
||||
let editorMenuItem = EditorMenuItem(
|
||||
title: "domain_list.open_in".localized(editor.name),
|
||||
action: #selector(self.openWithEditor(sender:)),
|
||||
for app in applications where app.type != .browser {
|
||||
let menuItem = ApplicationMenuItem(
|
||||
title: "domain_list.open_in".localized(app.name),
|
||||
action: #selector(self.openWithApp(sender:)),
|
||||
keyEquivalent: "",
|
||||
systemImage: "arrow.up.right"
|
||||
)
|
||||
editorMenuItem.editor = editor
|
||||
menu.addItem(editorMenuItem)
|
||||
|
||||
if let applicationPath = app.path {
|
||||
let icon = NSWorkspace.shared.icon(forFile: applicationPath)
|
||||
icon.size = NSSize(width: 16, height: 16)
|
||||
menuItem.image = icon
|
||||
}
|
||||
|
||||
menuItem.app = app
|
||||
menu.addItem(menuItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user