Add Konverter: a local-only menu bar file converter
Drag files in Finder, hold a modifier, and drop on a wheel of outputs. Conversions run entirely on-device through ImageIO, PDFKit, AVFoundation, the bundled CLI tools, and optional ffmpeg/LibreOffice. Includes a --self-test suite (41 checks) and a --convert CLI mode so the conversion matrix can be verified without the UI.
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct KonverterApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
// A menu bar app: no windows of its own. Settings are a custom window
|
||||
// so the app can be opened from the status item on any space.
|
||||
SwiftUI.Settings { EmptyView() }
|
||||
}
|
||||
}
|
||||
|
||||
final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
private let menuBar = MenuBarController()
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
if CommandLine.arguments.contains("--self-test") {
|
||||
exit(SelfTest.run())
|
||||
}
|
||||
if CommandLine.arguments.contains("--convert") {
|
||||
exit(CLI.run())
|
||||
}
|
||||
|
||||
terminateOtherInstances()
|
||||
|
||||
menuBar.install()
|
||||
|
||||
DragMonitor.shared.onBegan = { urls in
|
||||
WheelController.shared.begin(urls: urls)
|
||||
}
|
||||
DragMonitor.shared.onTick = { _, location, modifiers in
|
||||
guard Settings.shared.enabled else { return }
|
||||
WheelController.shared.tick(location: location, modifiers: modifiers)
|
||||
}
|
||||
DragMonitor.shared.onEnded = { _ in
|
||||
WheelController.shared.end()
|
||||
}
|
||||
DragMonitor.shared.onKey = { keyCode, modifiers in
|
||||
WheelController.shared.handleKey(keyCode, modifiers: modifiers)
|
||||
}
|
||||
DragMonitor.shared.start()
|
||||
|
||||
NSApp.servicesProvider = self
|
||||
NSUpdateDynamicServices()
|
||||
|
||||
// Warm the Quick Look machinery so the first Float is instant.
|
||||
DispatchQueue.main.async {
|
||||
_ = NSWorkspace.shared
|
||||
}
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
DragMonitor.shared.stop()
|
||||
WheelController.shared.cancel()
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
private func terminateOtherInstances() {
|
||||
let identifier = Bundle.main.bundleIdentifier ?? "dev.tzgyn.konverter"
|
||||
let mine = ProcessInfo.processInfo.processIdentifier
|
||||
for other in NSRunningApplication.runningApplications(withBundleIdentifier: identifier)
|
||||
where other.processIdentifier != mine {
|
||||
other.forceTerminate()
|
||||
}
|
||||
}
|
||||
|
||||
/// Finder Quick Action / Services entry. The wheel needs a pointer to sit
|
||||
/// under, so this opens the same options as a native menu.
|
||||
@objc func convertFiles(
|
||||
_ pasteboard: NSPasteboard,
|
||||
userData: String,
|
||||
error: AutoreleasingUnsafeMutablePointer<NSString>
|
||||
) {
|
||||
let urls = (pasteboard.readObjects(
|
||||
forClasses: [NSURL.self],
|
||||
options: [.urlReadingFileURLsOnly: true]
|
||||
) as? [URL]) ?? []
|
||||
guard !urls.isEmpty else {
|
||||
error.pointee = "No files were passed to Konverter." as NSString
|
||||
return
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
QuickMenu.show(urls: urls, at: NSEvent.mouseLocation)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user