import AppKit import ConjureKit @MainActor final class ApplicationActionsController: NSViewController, NSSearchFieldDelegate, NSTableViewDataSource, NSTableViewDelegate { private var search: ActionsSearchField! private var background: LauncherBackground! private var entries: [ActionEntry] = [] private var visibleEntries: [Int] = [] private var availableEntries = Array(0..<6) private var selectedEntry: Int? = 0 private var rowTopConstraints: [NSLayoutConstraint] = [] private var heightConstraint: NSLayoutConstraint? private var empty: NSTextField! private var applicationLabel: NSTextField! private var mouseMonitor: Any? private weak var coveredPill: NSView? private var coveredPillAlpha: CGFloat = 1 private var isClosing = false private var transitionGeneration = 0 private var panel: ActionsPanelView { view as! ActionsPanelView } var open: (() -> Void)? var copyEmoji: (() -> Void)? var toggleFavorite: (() -> Void)? var isFavorite = false var showInFinder: (() -> Void)? var resetRanking: (() -> Void)? var quit: (() -> Void)? var uninstall: (() -> Void)? var changeSourceLanguage: (() -> Void)? var changeTargetLanguage: (() -> Void)? var swapLanguages: (() -> Void)? var selectProvider: ((Bool) -> Void)? private var currentProviderEntry: Int? var selectLanguage: ((String) -> Void)? var selectNote: ((UUID) -> Void)? var browseNotes: (() -> Void)? var newNote: (() -> Void)? var deleteNote: (() -> Void)? var copyNoteMarkdown: (() -> Void)? var exportNoteMarkdown: (() -> Void)? private enum ChoiceID: Equatable { case language(String) case note(UUID) } private struct SelectionChoice { let id: ChoiceID let title: String let searchText: FuzzySearchText } private var choices: [SelectionChoice]? private var matchingChoices: [SelectionChoice] = [] private var currentChoice: ChoiceID? private var selectionTable: NSTableView? private var selectionScroll: NSScrollView? private var selectionViewportHeight: NSLayoutConstraint? private var selectionAccessibilityLabel = "" private var currentChoiceAccessibilityLabel = "" var closed: (() -> Void)? var isShown: Bool { viewIfLoaded?.superview != nil && !isClosing } init() { super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("Conjure creates actions programmatically") } override func loadView() { preparePresentation() } private func preparePresentation() { search = ActionsSearchField() background = LauncherBackground(frame: .zero) empty = NSTextField(labelWithString: "No matching actions") applicationLabel = NSTextField(labelWithString: "") rowTopConstraints = [] selectionTable = nil selectionScroll = nil selectionViewportHeight = nil entries = [ ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text), ActionEntry(title: "Favorite", symbol: "star", color: SunsetPalette.text), ActionEntry(title: "Show in Finder", symbol: "finder", color: SunsetPalette.text), ActionEntry(title: "Reset ranking", symbol: "arrow.counterclockwise", color: SunsetPalette.text), ActionEntry(title: "Quit application", symbol: "stop.circle", color: SunsetPalette.text), ActionEntry(title: "Uninstall application", symbol: "trash", color: .systemRed), ActionEntry(title: "Copy Emoji", symbol: "doc.on.doc", color: SunsetPalette.text), ActionEntry(title: "Change source language…", symbol: "character.bubble", color: SunsetPalette.text), ActionEntry(title: "Change target language…", symbol: "character.bubble", color: SunsetPalette.text), ActionEntry(title: "Swap languages", symbol: "arrow.left.arrow.right", color: SunsetPalette.text), ActionEntry(title: "MyMemory (no key)", symbol: "checkmark", color: SunsetPalette.text), ActionEntry(title: "OpenAI · gpt-6-luna", symbol: "checkmark", color: SunsetPalette.text), ActionEntry(title: "Browse Notes", symbol: "list.bullet", color: SunsetPalette.text, commandKey: "P"), ActionEntry(title: "New Note", symbol: "square.and.pencil", color: SunsetPalette.text, commandKey: "N"), ActionEntry(title: "Delete Note", symbol: "trash", color: .systemRed), ActionEntry(title: "Copy as Markdown", symbol: "doc.on.doc", color: SunsetPalette.text), ActionEntry(title: "Export as Markdown…", symbol: "square.and.arrow.up", color: SunsetPalette.text) ] view = ActionsPanelView() view.wantsLayer = true view.layer?.shadowColor = NSColor.black.cgColor view.layer?.shadowOpacity = 0.35 view.layer?.shadowRadius = 14 view.layer?.shadowOffset = NSSize(width: 0, height: -4) let content = NSVisualEffectView() content.material = .hudWindow content.blendingMode = .behindWindow content.state = .active roundMaterialCorners(content, radius: 18) content.layer?.borderWidth = 0.5 background.setAccessibilityElement(false) content.addSubview(background) applicationLabel.textColor = SunsetPalette.secondaryText applicationLabel.lineBreakMode = .byTruncatingTail applicationLabel.font = .systemFont(ofSize: 12, weight: .medium) search.font = .systemFont(ofSize: LauncherRowView.textSize) search.textColor = SunsetPalette.text search.placeholderAttributedString = NSAttributedString(string: "Search for actions…", attributes: [ .foregroundColor: SunsetPalette.secondaryText, .font: NSFont.systemFont(ofSize: LauncherRowView.textSize) ]) search.isBordered = false search.drawsBackground = false search.focusRingType = .none if let cell = search.cell as? NSSearchFieldCell { cell.searchButtonCell = nil cell.cancelButtonCell = nil } search.delegate = self search.sendsSearchStringImmediately = true search.setAccessibilityLabel("Search application actions") search.closeActions = { [weak self] in self?.close() } search.commandKey = { [weak self] key in self?.handleCommandKey(key) ?? false } empty.textColor = SunsetPalette.secondaryText empty.font = .systemFont(ofSize: 14) empty.isHidden = true content.translatesAutoresizingMaskIntoConstraints = false view.addSubview(content) for child in [background!, applicationLabel!, empty!, search!] { child.translatesAutoresizingMaskIntoConstraints = false if child !== background { content.addSubview(child) } } for (index, entry) in entries.enumerated() { entry.view.translatesAutoresizingMaskIntoConstraints = false content.addSubview(entry.view) entry.button.tag = index entry.button.target = self entry.button.action = #selector(chooseButton(_:)) entry.view.addGestureRecognizer(NSClickGestureRecognizer(target: self, action: #selector(chooseRow(_:)))) let top = entry.view.topAnchor.constraint(equalTo: applicationLabel.bottomAnchor, constant: 10 + CGFloat(index) * LauncherRowView.rowHeight) rowTopConstraints.append(top) NSLayoutConstraint.activate([ entry.view.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8), entry.view.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8), top ]) } let height = view.heightAnchor.constraint(equalToConstant: 116 + CGFloat(entries.count - 1) * LauncherRowView.rowHeight) heightConstraint = height NSLayoutConstraint.activate([ view.widthAnchor.constraint(equalToConstant: 360), height, content.leadingAnchor.constraint(equalTo: view.leadingAnchor), content.trailingAnchor.constraint(equalTo: view.trailingAnchor), content.topAnchor.constraint(equalTo: view.topAnchor), content.bottomAnchor.constraint(equalTo: view.bottomAnchor), background.leadingAnchor.constraint(equalTo: content.leadingAnchor), background.trailingAnchor.constraint(equalTo: content.trailingAnchor), background.topAnchor.constraint(equalTo: content.topAnchor), background.bottomAnchor.constraint(equalTo: content.bottomAnchor), applicationLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 16), applicationLabel.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -16), applicationLabel.topAnchor.constraint(equalTo: content.topAnchor, constant: 14), empty.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 16), empty.centerYAnchor.constraint(equalTo: entries[0].view.centerYAnchor), search.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 16), search.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -16), search.bottomAnchor.constraint(equalTo: content.bottomAnchor, constant: -8), search.heightAnchor.constraint(equalToConstant: 24) ]) content.layer?.borderColor = SunsetPalette.secondaryText.withAlphaComponent(0.22).cgColor if choices != nil { height.isActive = false let table = NSTableView() table.style = .plain table.addTableColumn(NSTableColumn(identifier: NSUserInterfaceItemIdentifier("Selection"))) table.headerView = nil table.backgroundColor = .clear table.selectionHighlightStyle = .none table.rowHeight = LauncherRowView.rowHeight table.intercellSpacing = .zero table.columnAutoresizingStyle = .lastColumnOnlyAutoresizingStyle table.autoresizingMask = [.width] table.focusRingType = .none table.dataSource = self table.delegate = self table.target = self table.action = #selector(chooseSelectionRow) table.setAccessibilityLabel(selectionAccessibilityLabel) let scroll = NSScrollView() scroll.documentView = table scroll.hasVerticalScroller = true scroll.autohidesScrollers = true scroll.drawsBackground = false scroll.translatesAutoresizingMaskIntoConstraints = false content.addSubview(scroll) let viewportHeight = scroll.heightAnchor.constraint(equalToConstant: LauncherRowView.rowHeight) NSLayoutConstraint.activate([ scroll.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8), scroll.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8), scroll.topAnchor.constraint(equalTo: applicationLabel.bottomAnchor, constant: 10), scroll.bottomAnchor.constraint(equalTo: search.topAnchor, constant: -8), viewportHeight, ]) selectionTable = table selectionScroll = scroll selectionViewportHeight = viewportHeight } updateSelection() } func show(for application: Application, relativeTo anchor: NSView) { show(title: application.name, relativeTo: anchor, available: application.command == nil ? Array(0..<6) : [0, 1, 3]) } func showEmoji(named name: String, relativeTo anchor: NSView) { show(title: name, relativeTo: anchor, available: [0, 6, 1], emoji: true, searchAccessibilityLabel: "Search emoji actions") } func showTranslation(relativeTo anchor: NSView) { show(title: "Translation", relativeTo: anchor, available: [7, 8, 9], searchAccessibilityLabel: "Search translation actions") } func showProviders(selectedOpenAI: Bool, relativeTo anchor: NSView) { currentProviderEntry = selectedOpenAI ? 11 : 10 show(title: "Translation provider", relativeTo: anchor, available: [10, 11], searchAccessibilityLabel: "Search translation providers", belowAnchor: true) if isShown { selectedEntry = currentProviderEntry updateSelection() } } func showLanguages(_ choices: [(code: String, name: String)], selected: String, source: Bool, relativeTo anchor: NSView) { currentChoice = .language(selected) selectionAccessibilityLabel = "Translation languages" currentChoiceAccessibilityLabel = "Current language" show(title: source ? "Source language" : "Target language", relativeTo: anchor, available: [], searchAccessibilityLabel: source ? "Search source languages" : "Search target languages", selectionChoices: choices.map { SelectionChoice(id: .language($0.code), title: $0.name, searchText: FuzzySearchText($0.name)) }, selectionName: "languages") } func showNotes(_ notes: [(id: UUID, title: String)], selected: UUID?, relativeTo anchor: NSView) { currentChoice = selected.map { .note($0) } selectionAccessibilityLabel = "Notes" currentChoiceAccessibilityLabel = "Current note" show(title: "Browse Notes", relativeTo: anchor, available: [], searchAccessibilityLabel: "Search notes", selectionChoices: notes.map { SelectionChoice(id: .note($0.id), title: $0.title, searchText: FuzzySearchText($0.title)) }, selectionName: "notes") } func showNotesActions(relativeTo anchor: NSView) { show(title: "Notes", relativeTo: anchor, available: [12, 13, 14, 15, 16], searchAccessibilityLabel: "Search notes actions") } private func show(title: String, relativeTo anchor: NSView, available: [Int], emoji: Bool = false, searchAccessibilityLabel: String = "Search application actions", belowAnchor: Bool = false, selectionChoices: [SelectionChoice]? = nil, selectionName: String = "") { if viewIfLoaded?.superview != nil { if isShown, availableEntries == available, applicationLabel.stringValue == title { close(); return } close(animated: false) } guard let window = anchor.window, let host = window.contentView else { return } let pill = anchor.superview ?? anchor choices = selectionChoices // Reuse behavior, not native drawing state from a differently sized popup. if isViewLoaded { preparePresentation() } else { _ = view } applicationLabel.stringValue = title availableEntries = available let openTitle = emoji ? "Copy and Paste" : "Open" entries[0].button.title = openTitle entries[0].button.setAccessibilityLabel(openTitle) entries[0].icon.image = NSImage(systemSymbolName: emoji ? "doc.on.clipboard" : "arrow.up.forward.app", accessibilityDescription: nil) search.setAccessibilityLabel(searchAccessibilityLabel) empty.stringValue = choices == nil ? "No matching actions" : "No matching \(selectionName)" let favorite = entries[1] let favoriteTitle = isFavorite ? "Remove Favorite" : "Favorite" favorite.button.attributedTitle = NSAttributedString(string: favoriteTitle, attributes: [ .foregroundColor: SunsetPalette.text, .font: NSFont.systemFont(ofSize: LauncherRowView.textSize, weight: .medium) ]) favorite.button.setAccessibilityLabel(favoriteTitle) favorite.icon.image = NSImage(systemSymbolName: isFavorite ? "star.fill" : "star", accessibilityDescription: nil) for index in [10, 11] { entries[index].icon.isHidden = currentProviderEntry != index } search.placeholderAttributedString = NSAttributedString( string: choices != nil ? "Search for \(selectionName)…" : belowAnchor ? "Search for providers…" : "Search for actions…", attributes: [.foregroundColor: SunsetPalette.secondaryText, .font: NSFont.systemFont(ofSize: LauncherRowView.textSize)]) search.stringValue = "" controlTextDidChange(Notification(name: NSControl.textDidChangeNotification)) transitionGeneration += 1 isClosing = false panel.acceptsInteraction = true panel.setAccessibilityHidden(false) if view.superview == nil { view.alphaValue = 0 view.translatesAutoresizingMaskIntoConstraints = false host.addSubview(view) // Paint only the popup's bounds, using the window's gradient coordinates. background.canvas = host NSLayoutConstraint.activate([ view.trailingAnchor.constraint(equalTo: pill.trailingAnchor), belowAnchor ? view.topAnchor.constraint(equalTo: pill.topAnchor) : view.bottomAnchor.constraint(equalTo: pill.bottomAnchor) ]) coveredPill = pill coveredPillAlpha = pill.alphaValue } coveredPill?.setAccessibilityHidden(true) host.layoutSubtreeIfNeeded() background.needsDisplay = true if let selectedEntry { selectionTable?.scrollRowToVisible(selectedEntry) } NSAnimationContext.runAnimationGroup { context in context.duration = NSWorkspace.shared.accessibilityDisplayShouldReduceMotion ? 0 : 0.16 view.animator().alphaValue = 1 coveredPill?.animator().alphaValue = 0 } window.makeFirstResponder(search) (search.currentEditor() as? NSTextView)?.insertionPointColor = SunsetPalette.accent mouseMonitor = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseDown, .rightMouseDown]) { [weak self, weak pill] event in MainActor.assumeIsolated { guard let self, self.isShown, event.window === self.view.window else { return } let point = self.view.convert(event.locationInWindow, from: nil) let pillPoint = pill?.convert(event.locationInWindow, from: nil) if !self.view.bounds.contains(point), !(pillPoint.map { pill?.bounds.contains($0) == true } ?? false) { self.close() } } return event } } func close(animated: Bool = true) { guard viewIfLoaded?.superview != nil, !isClosing || !animated else { return } transitionGeneration += 1 let generation = transitionGeneration isClosing = true panel.acceptsInteraction = false panel.setAccessibilityHidden(true) if let mouseMonitor { NSEvent.removeMonitor(mouseMonitor) } mouseMonitor = nil coveredPill?.setAccessibilityHidden(false) if !animated { view.removeFromSuperview() coveredPill?.layer?.removeAllAnimations() coveredPill?.alphaValue = coveredPillAlpha coveredPill = nil isClosing = false closed?() return } // Finish teardown even if the presenting Command releases this controller. NSAnimationContext.runAnimationGroup { context in context.duration = NSWorkspace.shared.accessibilityDisplayShouldReduceMotion ? 0 : 0.12 view.animator().alphaValue = 0 coveredPill?.animator().alphaValue = coveredPillAlpha } completionHandler: { [self] in MainActor.assumeIsolated { guard self.transitionGeneration == generation, self.isClosing else { return } self.view.removeFromSuperview() self.coveredPill = nil self.isClosing = false } } closed?() } func controlTextDidChange(_ notification: Notification) { let query = search.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) if let choices, let table = selectionTable { matchingChoices = FuzzySearchText.results(for: query, in: choices, text: \.searchText) { $0.title.localizedStandardCompare($1.title) == .orderedAscending } let selection = (query.isEmpty ? matchingChoices.firstIndex(where: { $0.id == currentChoice }) : nil) ?? (matchingChoices.isEmpty ? nil : 0) for entry in entries { entry.view.isHidden = true } selectionViewportHeight?.constant = CGFloat(max(min(matchingChoices.count, 6), 1)) * table.rowHeight empty.isHidden = !matchingChoices.isEmpty selectionScroll?.isHidden = matchingChoices.isEmpty view.superview?.layoutSubtreeIfNeeded() table.reloadData() selectedEntry = selection if let selectedEntry { table.selectRowIndexes(IndexSet(integer: selectedEntry), byExtendingSelection: false) table.scrollRowToVisible(selectedEntry) } updateSelection() return } visibleEntries = availableEntries.filter { query.isEmpty || entries[$0].button.title.range(of: query, options: [.caseInsensitive, .diacriticInsensitive]) != nil } selectedEntry = visibleEntries.first for index in entries.indices { let position = visibleEntries.firstIndex(of: index) entries[index].view.isHidden = position == nil rowTopConstraints[index].constant = 10 + CGFloat(position ?? 0) * LauncherRowView.rowHeight } heightConstraint?.constant = 116 + CGFloat(max(visibleEntries.count, 1) - 1) * LauncherRowView.rowHeight empty.isHidden = !visibleEntries.isEmpty updateSelection() } func moveSelection(by offset: Int) { if let table = selectionTable { guard !matchingChoices.isEmpty else { return } let next = min(max((selectedEntry ?? 0) + offset, 0), matchingChoices.count - 1) table.selectRowIndexes(IndexSet(integer: next), byExtendingSelection: false) table.scrollRowToVisible(next) return } guard !visibleEntries.isEmpty else { return } let current = selectedEntry.flatMap { visibleEntries.firstIndex(of: $0) } ?? 0 selectedEntry = visibleEntries[min(max(current + offset, 0), visibleEntries.count - 1)] updateSelection() } private func updateSelection() { if let table = selectionTable { let visible = table.rows(in: table.visibleRect) for row in visible.location.. Bool { handle(selector) } func handle(_ selector: Selector) -> Bool { switch selector { case #selector(NSResponder.cancelOperation(_:)): guard isShown else { return false } close() case #selector(NSResponder.insertNewline(_:)): if let selectedEntry { chooseAction(at: selectedEntry) } case #selector(NSResponder.moveUp(_:)): moveSelection(by: -1) case #selector(NSResponder.moveDown(_:)): moveSelection(by: 1) default: return false } return true } private func handleCommandKey(_ key: String) -> Bool { guard isShown, availableEntries == [12, 13, 14, 15, 16] else { return false } switch key { case "p": close(); browseNotes?() case "n": close(); newNote?() default: return false } return true } func numberOfRows(in tableView: NSTableView) -> Int { matchingChoices.count } func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { let identifier = NSUserInterfaceItemIdentifier("SelectionCell") let cell = tableView.makeView(withIdentifier: identifier, owner: self) as? SelectionCell ?? SelectionCell() cell.identifier = identifier let choice = matchingChoices[row] cell.entry.button.title = choice.title cell.entry.button.setAccessibilityLabel(choice.title) cell.entry.button.setAccessibilityValue(choice.id == currentChoice ? currentChoiceAccessibilityLabel : "") cell.entry.button.target = self cell.entry.button.action = #selector(chooseButton(_:)) cell.entry.button.tag = row cell.entry.icon.isHidden = choice.id != currentChoice cell.entry.view.isSelected = selectedEntry == row cell.entry.returnKey.isHidden = selectedEntry != row return cell } func tableViewSelectionDidChange(_ notification: Notification) { guard let table = selectionTable else { return } selectedEntry = table.selectedRow >= 0 ? table.selectedRow : nil updateSelection() } @objc private func chooseSelectionRow() { if let selectedEntry { chooseAction(at: selectedEntry) } } @objc private func chooseButton(_ sender: NSButton) { chooseAction(at: sender.tag) } @objc private func chooseRow(_ sender: NSGestureRecognizer) { if let index = entries.firstIndex(where: { $0.view === sender.view }) { chooseAction(at: index) } } private func chooseAction(at index: Int) { guard isShown else { return } if choices != nil { guard matchingChoices.indices.contains(index) else { return } let id = matchingChoices[index].id close() switch id { case .language(let code): selectLanguage?(code) case .note(let id): selectNote?(id) } return } guard visibleEntries.contains(index) else { return } close() switch index { case 0: open?() case 1: toggleFavorite?() case 2: showInFinder?() case 3: resetRanking?() case 4: quit?() case 5: uninstall?() case 6: copyEmoji?() case 7: changeSourceLanguage?() case 8: changeTargetLanguage?() case 9: swapLanguages?() case 10: selectProvider?(false) case 11: selectProvider?(true) case 12: browseNotes?() case 13: newNote?() case 14: deleteNote?() case 15: copyNoteMarkdown?() case 16: exportNoteMarkdown?() default: break } } } @MainActor private struct ActionEntry { let view = LauncherRowView() let button: NSButton let icon: IconView let returnKey: ShortcutIndicator let hasCommandKey: Bool init(title: String, symbol: String, color: NSColor, commandKey: String? = nil) { hasCommandKey = commandKey != nil returnKey = commandKey.map { ShortcutIndicator("⌘", $0) } ?? ShortcutIndicator("↵") let font = NSFont.systemFont(ofSize: LauncherRowView.textSize, weight: .medium) icon = IconView(font: font) button = NSButton(title: title, target: nil, action: nil) button.isBordered = false button.alignment = .left button.font = font button.contentTintColor = color button.attributedTitle = NSAttributedString(string: title, attributes: [ .foregroundColor: color, .font: font ]) button.setAccessibilityLabel(title) icon.image = NSImage(systemSymbolName: symbol, accessibilityDescription: nil) ?? (symbol == "finder" ? NSWorkspace.shared.icon(forFile: "/System/Library/CoreServices/Finder.app") : nil) icon.contentTintColor = color for child in [icon, button, returnKey] { child.translatesAutoresizingMaskIntoConstraints = false view.addSubview(child) } NSLayoutConstraint.activate([ view.heightAnchor.constraint(equalToConstant: LauncherRowView.rowHeight), icon.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 8), icon.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.leadingAnchor.constraint(equalTo: icon.trailingAnchor, constant: LauncherRowView.iconToTextSpacing), button.centerYAnchor.constraint(equalTo: view.centerYAnchor), button.trailingAnchor.constraint(equalTo: returnKey.leadingAnchor, constant: -12), returnKey.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -12), returnKey.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } } @MainActor private final class SelectionCell: NSView { let entry = ActionEntry(title: "", symbol: "checkmark", color: SunsetPalette.text) init() { super.init(frame: .zero) entry.view.translatesAutoresizingMaskIntoConstraints = false addSubview(entry.view) NSLayoutConstraint.activate([ entry.view.leadingAnchor.constraint(equalTo: leadingAnchor), entry.view.trailingAnchor.constraint(equalTo: trailingAnchor), entry.view.topAnchor.constraint(equalTo: topAnchor), entry.view.bottomAnchor.constraint(equalTo: bottomAnchor), ]) } required init?(coder: NSCoder) { fatalError("Conjure creates selection rows programmatically") } } @MainActor private final class ActionsPanelView: NSView { var acceptsInteraction = true override func hitTest(_ point: NSPoint) -> NSView? { acceptsInteraction ? super.hitTest(point) : nil } } @MainActor private final class ActionsSearchField: NSSearchField { var closeActions: (() -> Void)? var commandKey: ((String) -> Bool)? override func performKeyEquivalent(with event: NSEvent) -> Bool { if event.modifierFlags.intersection([.command, .control, .option, .shift]) == .command, let key = event.charactersIgnoringModifiers?.lowercased() { if key == "k" { closeActions?() return true } if commandKey?(key) == true { return true } } return super.performKeyEquivalent(with: event) } }