import Cocoa import WebKit import SwiftUI class WebRuleEditorViewController: NSViewController, WKNavigationDelegate, WKScriptMessageHandler { private var webView: WKWebView! private var editingRule: YabaiRule? var onSave: ((YabaiRule) -> Void)? var onCancel: (() -> Void)? init(editingRule: YabaiRule? = nil) { self.editingRule = editingRule super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func loadView() { let webConfiguration = WKWebViewConfiguration() let userController = WKUserContentController() // Add script message handlers for communication userController.add(self, name: "saveRule") userController.add(self, name: "cancelRule") webConfiguration.userContentController = userController webView = WKWebView(frame: .zero, configuration: webConfiguration) webView.navigationDelegate = self // Enable keyboard input and focus webView.setValue(false, forKey: "drawsBackground") self.view = webView } override func viewDidLoad() { super.viewDidLoad() // Load the HTML content if let htmlPath = Bundle.main.path(forResource: "rule-editor", ofType: "html") { let htmlUrl = URL(fileURLWithPath: htmlPath) webView.loadFileURL(htmlUrl, allowingReadAccessTo: htmlUrl.deletingLastPathComponent()) } else { // Fallback: inline HTML loadInlineHTML() } // Ensure the web view can receive keyboard input webView.becomeFirstResponder() } private func loadInlineHTML() { let html = """

Matching Criteria

Actions

""" webView.loadHTMLString(html, baseURL: nil) } // MARK: - WKScriptMessageHandler func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { case "saveRule": if let ruleDict = message.body as? [String: Any] { // Extract values to make type-checking easier let app = ruleDict["app"] as? String let title = ruleDict["title"] as? String let role = ruleDict["role"] as? String let subrole = ruleDict["subrole"] as? String let manage = (ruleDict["manage"] as? Bool ?? true) ? YabaiRule.ManageState.on : YabaiRule.ManageState.off let sticky = (ruleDict["sticky"] as? Bool ?? false) ? YabaiRule.StickyState.on : YabaiRule.StickyState.off let mouseFollowsFocus = ruleDict["mouse_follows_focus"] as? Bool ?? false let layerString = ruleDict["layer"] as? String ?? "normal" let layer = YabaiRule.WindowLayer(rawValue: layerString) ?? YabaiRule.WindowLayer.normal let opacity = ruleDict["opacity"] as? Double ?? 1.0 let border = (ruleDict["border"] as? Bool ?? false) ? YabaiRule.BorderState.on : YabaiRule.BorderState.off let rule = YabaiRule( app: app, title: title, role: role, subrole: subrole, manage: manage, sticky: sticky, mouseFollowsFocus: mouseFollowsFocus, layer: layer, opacity: opacity, border: border ) onSave?(rule) dismissController() } case "cancelRule": onCancel?() dismissController() default: break } } private func dismissController() { if let window = view.window { window.close() } } // MARK: - WKNavigationDelegate func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { // Web view loaded, ensure it can receive keyboard input DispatchQueue.main.async { webView.window?.makeFirstResponder(webView) } } } // MARK: - Window Controller class WebRuleEditorWindowController: NSWindowController, NSWindowDelegate { private var webViewController: WebRuleEditorViewController init(editingRule: YabaiRule? = nil) { webViewController = WebRuleEditorViewController(editingRule: editingRule) let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 500, height: 600), styleMask: [.titled, .closable], backing: .buffered, defer: false ) super.init(window: window) window.center() window.title = editingRule == nil ? "Create Rule" : "Edit Rule" window.contentViewController = webViewController window.isReleasedWhenClosed = false window.delegate = self webViewController.onSave = { [weak self] rule in // Forward to parent window controller if needed self?.onSave?(rule) } webViewController.onCancel = { [weak self] in self?.onCancel?() } } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } var onSave: ((YabaiRule) -> Void)? var onCancel: (() -> Void)? func showWindow() { window?.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) // Ensure web view gets focus DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { if let webView = self.webViewController.view as? WKWebView { self.window?.makeFirstResponder(webView) } } } // NSWindowDelegate method func windowWillClose(_ notification: Notification) { onCancel?() } }