fix: align action popup background with launcher

This commit is contained in:
2026-10-17 00:35:35 +02:00
parent a891d49bb1
commit 8766584fe3
4 changed files with 49 additions and 6 deletions
+2
View File
@@ -61,6 +61,8 @@ The launcher opens without a window animation; Settings is constructed only when
Right-click the launcher background for Settings and Quit. Actions include favorites, resetting usage ranking, revealing applications in Finder, quitting them, and uninstalling them.
The Actions surface crops the launcher’s full-size background at its own position, keeping the glows continuous without showing the results underneath.
### Global shortcuts and launch at login
In Settings, choose a **Global shortcut destination**: **Applications**, **Conjure Emoji**, or **Conjure Translate**. Choose **Record Shortcut…** and press a combination containing Command, Option, or Control. For example, select **Conjure Emoji** and record Command-Shift-Space to open the emoji picker directly. **Clear** disables only the selected destination's shortcut. No shortcuts are assigned by default; existing launcher bindings are preserved.
@@ -4,6 +4,7 @@ import ConjureKit
@MainActor
final class ApplicationActionsController: NSViewController, NSSearchFieldDelegate {
private let search = ActionsSearchField()
private let background = LauncherBackground(frame: .zero)
private let entries = [
ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text),
ActionEntry(title: "Favorite", symbol: "star", color: SunsetPalette.text),
@@ -49,11 +50,10 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
view.layer?.shadowOffset = NSSize(width: 0, height: -4)
let content = NSVisualEffectView()
content.material = .hudWindow
content.blendingMode = .withinWindow
content.blendingMode = .behindWindow
content.state = .active
roundMaterialCorners(content, radius: 18)
content.layer?.borderWidth = 0.5
let background = LauncherBackground(frame: .zero)
background.setAccessibilityElement(false)
content.addSubview(background)
applicationLabel.textColor = SunsetPalette.secondaryText
@@ -108,10 +108,6 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
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),
@@ -164,6 +160,13 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
view.alphaValue = 0
view.translatesAutoresizingMaskIntoConstraints = false
host.addSubview(view)
// Crop the window-sized background; filtering must not rescale its glows.
NSLayoutConstraint.activate([
background.leadingAnchor.constraint(equalTo: host.leadingAnchor),
background.trailingAnchor.constraint(equalTo: host.trailingAnchor),
background.topAnchor.constraint(equalTo: host.topAnchor),
background.bottomAnchor.constraint(equalTo: host.bottomAnchor)
])
NSLayoutConstraint.activate([
view.trailingAnchor.constraint(equalTo: pill.trailingAnchor),
view.bottomAnchor.constraint(equalTo: pill.bottomAnchor)
+7
View File
@@ -2,5 +2,12 @@
- the window should scroll with the current selection
- [x] make escape close the window that was opened, be it the application launcher, the emoji picker, or something else.
- clicking the "back" chevron should still take you back to the application launcher view though (as it does today).
- [ ] remove window open/fade in/transition animation when opening conjure from keybindings
- this is already the case for the application launcher window.
- evidently not the case for the emoji picker (though it should be ubiquitous)
- [ ] add a notes app inspired by raycast notes
- support multiple different notes
- support switching between notes
- highlight markdown syntax
- allow images
- [ ] improve on the conjure translation command
@@ -0,0 +1,31 @@
import AppKit
import ConjureKit
import Testing
@testable import Conjure
@Test @MainActor func actionsBackgroundKeepsWindowCoordinates() throws {
_ = NSApplication.shared
let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 750, height: 480),
styleMask: [.borderless], backing: .buffered, defer: false)
let host = try #require(window.contentView)
let anchor = NSView(frame: NSRect(x: 700, y: 20, width: 30, height: 30))
host.addSubview(anchor)
let actions = ApplicationActionsController()
defer { actions.close(animated: false) }
func descendants(_ view: NSView) -> [NSView] { [view] + view.subviews.flatMap(descendants) }
for width in [750.0, 900.0] {
window.setContentSize(NSSize(width: width, height: 480))
actions.showEmoji(named: "Smile", relativeTo: anchor)
let background = try #require(descendants(host).compactMap { $0 as? LauncherBackground }.first)
let search = try #require(descendants(actions.view).compactMap { $0 as? NSSearchField }.first)
for query in ["", "Copy Emoji", "no matching action"] {
search.stringValue = query
actions.controlTextDidChange(Notification(name: NSControl.textDidChangeNotification, object: search))
host.layoutSubtreeIfNeeded()
let rect = background.convert(background.bounds, to: host)
#expect(rect == host.bounds)
#expect(background.bounds.size != actions.view.bounds.size)
}
actions.close(animated: false)
}
}