Files
konverter/Sources/Konverter/App.swift
T
TZGyn 2d031e58a4 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.
2026-10-20 00:36:40 +08:00

92 lines
2.9 KiB
Swift

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)
}
}
}