fix: use standard window chrome for notes
This commit is contained in:
@@ -137,7 +137,7 @@ Commands explicitly declare `Application.Command.presentsView`. The shared dispa
|
|||||||
|
|
||||||
Search for **Conjure Notes**, identified by its centered 📔 icon, or assign it a global shortcut in Settings. Notes keeps a compact 570 × 404 layout inspired by Raycast Notes, with rounded corners and a centered muted note title, while using Conjure’s shared warm gradient background, cream text, and amber list markers and caret. The title bar offers Close, **Actions** (⌘), **Browse Notes** (note icon), and **New Note** (+). Browse Notes (Command-P) opens Conjure’s styled, scrollable searchable picker below the title bar, shared with translation language selection—not a native menu. Fuzzy-search note titles, then select with Up/Down, Control-J/K, and Return or by clicking a note. A checkmark marks the current note, which is selected and scrolled into view on opening. Reopening Notes restores the last selection. Each note’s first nonempty line becomes its title; duplicate titles remain independently selectable.
|
Search for **Conjure Notes**, identified by its centered 📔 icon, or assign it a global shortcut in Settings. Notes keeps a compact 570 × 404 layout inspired by Raycast Notes, with rounded corners and a centered muted note title, while using Conjure’s shared warm gradient background, cream text, and amber list markers and caret. The title bar offers Close, **Actions** (⌘), **Browse Notes** (note icon), and **New Note** (+). Browse Notes (Command-P) opens Conjure’s styled, scrollable searchable picker below the title bar, shared with translation language selection—not a native menu. Fuzzy-search note titles, then select with Up/Down, Control-J/K, and Return or by clicking a note. A checkmark marks the current note, which is selected and scrolled into view on opening. Reopening Notes restores the last selection. Each note’s first nonempty line becomes its title; duplicate titles remain independently selectable.
|
||||||
|
|
||||||
Notes uses native macOS window decorations with only the Close button visible; minimize and zoom are hidden. Closing still saves first and keeps the window open if saving fails. Other launcher views remain borderless.
|
Notes uses a standard macOS window with system-inherited appearance and only the native Close button visible; minimize and zoom are hidden. The editor retains Conjure’s warm palette. Closing still saves first and keeps the window open if saving fails. Other launcher views remain borderless with their existing dark appearance.
|
||||||
|
|
||||||
Actions (Command-K) opens the same searchable popup style used throughout Conjure, with **Browse Notes** (Command-P) and **New Note** (Command-N). Command-N creates a note while the editor or either popup is focused. Command-P toggles Browse Notes even while its title-bar controls are covered. Escape or clicking outside closes an open popup and returns focus to the editor without leaving Notes; another Escape dismisses the launcher. Control-J/K navigates only while a popup is open, preserving normal editor commands when it is closed.
|
Actions (Command-K) opens the same searchable popup style used throughout Conjure, with **Browse Notes** (Command-P) and **New Note** (Command-N). Command-N creates a note while the editor or either popup is focused. Command-P toggles Browse Notes even while its title-bar controls are covered. Escape or clicking outside closes an open popup and returns focus to the editor without leaving Notes; another Escape dismisses the launcher. Control-J/K navigates only while a popup is open, preserving normal editor commands when it is closed.
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
self.pasteboard = pasteboard
|
self.pasteboard = pasteboard
|
||||||
self.notesDirectory = notesDirectory
|
self.notesDirectory = notesDirectory
|
||||||
self.toggleSystemTheme = toggleSystemTheme
|
self.toggleSystemTheme = toggleSystemTheme
|
||||||
let window = LauncherPanel(
|
let window = LauncherWindow(
|
||||||
contentRect: NSRect(x: 0, y: 0, width: 750, height: 474),
|
contentRect: NSRect(x: 0, y: 0, width: 750, height: 474),
|
||||||
styleMask: [.borderless],
|
styleMask: [.borderless],
|
||||||
backing: .buffered,
|
backing: .buffered,
|
||||||
@@ -148,7 +148,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
}
|
}
|
||||||
guard closeCommand() else { return }
|
guard closeCommand() else { return }
|
||||||
actions.close(animated: false)
|
actions.close(animated: false)
|
||||||
(window as? LauncherPanel)?.beginNumberHintSession()
|
(window as? LauncherWindow)?.beginNumberHintSession()
|
||||||
let frontmost = NSWorkspace.shared.frontmostApplication
|
let frontmost = NSWorkspace.shared.frontmostApplication
|
||||||
if frontmost?.processIdentifier != ProcessInfo.processInfo.processIdentifier {
|
if frontmost?.processIdentifier != ProcessInfo.processInfo.processIdentifier {
|
||||||
previousApplication = frontmost
|
previousApplication = frontmost
|
||||||
@@ -165,7 +165,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
window?.makeKeyAndOrderFront(nil)
|
window?.makeKeyAndOrderFront(nil)
|
||||||
if let notes { notes.focusEditor() }
|
if let notes { notes.focusEditor() }
|
||||||
else { window?.makeFirstResponder(searchField) }
|
else { window?.makeFirstResponder(searchField) }
|
||||||
(window as? LauncherPanel)?.sampleNumberHints()
|
(window as? LauncherWindow)?.sampleNumberHints()
|
||||||
(searchField.currentEditor() as? NSTextView)?.insertionPointColor = SunsetPalette.accent
|
(searchField.currentEditor() as? NSTextView)?.insertionPointColor = SunsetPalette.accent
|
||||||
refreshApplications()
|
refreshApplications()
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
func windowDidResignKey(_ notification: Notification) {
|
func windowDidResignKey(_ notification: Notification) {
|
||||||
(window as? LauncherPanel)?.resetNumberHints()
|
(window as? LauncherWindow)?.resetNumberHints()
|
||||||
guard !isLaunching, launcherFrameBeforeReview == nil, window?.attachedSheet == nil else { return }
|
guard !isLaunching, launcherFrameBeforeReview == nil, window?.attachedSheet == nil else { return }
|
||||||
dismiss(restoringFocus: false)
|
dismiss(restoringFocus: false)
|
||||||
}
|
}
|
||||||
@@ -440,7 +440,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
currency.update(query)
|
currency.update(query)
|
||||||
isCurrencyCalculation = currency.isCurrency
|
isCurrencyCalculation = currency.isCurrency
|
||||||
calculation = currency.result
|
calculation = currency.result
|
||||||
if calculation != nil { (window as? LauncherPanel)?.resetNumberHints() }
|
if calculation != nil { (window as? LauncherWindow)?.resetNumberHints() }
|
||||||
if calculation != nil {
|
if calculation != nil {
|
||||||
results = []
|
results = []
|
||||||
} else if searchField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
} else if searchField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
||||||
@@ -470,7 +470,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
statusLabel.stringValue = "\(results.count - commandCount) applications · \(commandCount) commands"
|
statusLabel.stringValue = "\(results.count - commandCount) applications · \(commandCount) commands"
|
||||||
}
|
}
|
||||||
updateOpenButton()
|
updateOpenButton()
|
||||||
(window as? LauncherPanel)?.sampleNumberHints()
|
(window as? LauncherWindow)?.sampleNumberHints()
|
||||||
}
|
}
|
||||||
|
|
||||||
func controlTextDidChange(_ notification: Notification) {
|
func controlTextDidChange(_ notification: Notification) {
|
||||||
@@ -613,7 +613,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func configureNumberHint(_ cell: ApplicationCell, row: Int, animated: Bool = false) {
|
private func configureNumberHint(_ cell: ApplicationCell, row: Int, animated: Bool = false) {
|
||||||
let visible = (window as? LauncherPanel)?.numberHintsVisible == true
|
let visible = (window as? LauncherWindow)?.numberHintsVisible == true
|
||||||
&& calculation == nil && row < 9 && row < resultCount && !isCommandOpen
|
&& calculation == nil && row < 9 && row < resultCount && !isCommandOpen
|
||||||
cell.setNumberHint(visible ? String(row + 1) : nil, animated: animated)
|
cell.setNumberHint(visible ? String(row + 1) : nil, animated: animated)
|
||||||
cell.setAccessibilityHelp(visible ? "Command-\(row + 1) opens this entry" : nil)
|
cell.setAccessibilityHelp(visible ? "Command-\(row + 1) opens this entry" : nil)
|
||||||
@@ -638,7 +638,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
@objc private func showActions() {
|
@objc private func showActions() {
|
||||||
guard !isLaunching, launcherFrameBeforeReview == nil, window?.attachedSheet == nil, results.indices.contains(tableView.selectedRow) else { return }
|
guard !isLaunching, launcherFrameBeforeReview == nil, window?.attachedSheet == nil, results.indices.contains(tableView.selectedRow) else { return }
|
||||||
guard !isCommandOpen else { return }
|
guard !isCommandOpen else { return }
|
||||||
(window as? LauncherPanel)?.resetNumberHints()
|
(window as? LauncherWindow)?.resetNumberHints()
|
||||||
let application = results[tableView.selectedRow]
|
let application = results[tableView.selectedRow]
|
||||||
actions.open = { [weak self] in self?.openApplication(application) }
|
actions.open = { [weak self] in self?.openApplication(application) }
|
||||||
actions.isFavorite = history.isFavorite(application)
|
actions.isFavorite = history.isFavorite(application)
|
||||||
@@ -687,7 +687,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
review.view.bottomAnchor.constraint(equalTo: content.bottomAnchor)
|
review.view.bottomAnchor.constraint(equalTo: content.bottomAnchor)
|
||||||
])
|
])
|
||||||
for view in launcherViews { view.setAccessibilityHidden(true) }
|
for view in launcherViews { view.setAccessibilityHidden(true) }
|
||||||
(window as? LauncherPanel)?.resetNumberHints()
|
(window as? LauncherWindow)?.resetNumberHints()
|
||||||
openButton.keyEquivalent = ""
|
openButton.keyEquivalent = ""
|
||||||
updateOpenButton()
|
updateOpenButton()
|
||||||
var frame = window.frame
|
var frame = window.frame
|
||||||
@@ -758,7 +758,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
executeCommand(command, dismissOnSuccess: true)
|
executeCommand(command, dismissOnSuccess: true)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
(window as? LauncherPanel)?.resetNumberHints()
|
(window as? LauncherWindow)?.resetNumberHints()
|
||||||
isLaunching = true
|
isLaunching = true
|
||||||
updateOpenButton()
|
updateOpenButton()
|
||||||
Task {
|
Task {
|
||||||
@@ -915,6 +915,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
self.launcherViews.first?.setAccessibilityHidden(false)
|
self.launcherViews.first?.setAccessibilityHidden(false)
|
||||||
if command == .notes, let previousFrame {
|
if command == .notes, let previousFrame {
|
||||||
self.window?.styleMask = [.borderless]
|
self.window?.styleMask = [.borderless]
|
||||||
|
self.window?.appearance = NSAppearance(named: .darkAqua)
|
||||||
self.window?.setFrame(previousFrame, display: true)
|
self.window?.setFrame(previousFrame, display: true)
|
||||||
if let material = content as? NSVisualEffectView { roundMaterialCorners(material, radius: 24) }
|
if let material = content as? NSVisualEffectView { roundMaterialCorners(material, radius: 24) }
|
||||||
}
|
}
|
||||||
@@ -938,7 +939,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
emojiPicker?.closed = close
|
emojiPicker?.closed = close
|
||||||
notes?.closed = close
|
notes?.closed = close
|
||||||
actions.close(animated: false)
|
actions.close(animated: false)
|
||||||
(window as? LauncherPanel)?.resetNumberHints()
|
(window as? LauncherWindow)?.resetNumberHints()
|
||||||
openButton.keyEquivalent = ""
|
openButton.keyEquivalent = ""
|
||||||
searchSymbol.isHidden = true
|
searchSymbol.isHidden = true
|
||||||
commandBack.setAccessibilityLabel("Back to applications")
|
commandBack.setAccessibilityLabel("Back to applications")
|
||||||
@@ -953,6 +954,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
|||||||
)
|
)
|
||||||
searchField.setAccessibilityLabel(accessibilityLabel)
|
searchField.setAccessibilityLabel(accessibilityLabel)
|
||||||
if command == .notes {
|
if command == .notes {
|
||||||
|
window?.appearance = nil
|
||||||
window?.styleMask = [.titled, .closable, .fullSizeContentView]
|
window?.styleMask = [.titled, .closable, .fullSizeContentView]
|
||||||
window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
window?.standardWindowButton(.miniaturizeButton)?.isHidden = true
|
||||||
window?.standardWindowButton(.zoomButton)?.isHidden = true
|
window?.standardWindowButton(.zoomButton)?.isHidden = true
|
||||||
@@ -1214,7 +1216,7 @@ private final class ApplicationTableView: NSTableView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class LauncherPanel: NSPanel {
|
final class LauncherWindow: NSWindow {
|
||||||
override var canBecomeKey: Bool { true }
|
override var canBecomeKey: Bool { true }
|
||||||
override var canBecomeMain: Bool { true }
|
override var canBecomeMain: Bool { true }
|
||||||
var navigateSelection: ((Int) -> Void)?
|
var navigateSelection: ((Int) -> Void)?
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ import Testing
|
|||||||
#expect(search.accessibilityLabel() == "English text to translate into Japanese")
|
#expect(search.accessibilityLabel() == "English text to translate into Japanese")
|
||||||
_ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.cancelOperation(_:)))
|
_ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.cancelOperation(_:)))
|
||||||
#expect(history.suggestions(in: [command, competitor]).first?.id == competitor.id)
|
#expect(history.suggestions(in: [command, competitor]).first?.id == competitor.id)
|
||||||
let panel = try #require(launcher.window as? LauncherPanel)
|
let panel = try #require(launcher.window as? LauncherWindow)
|
||||||
panel.launchNumber?(0)
|
panel.launchNumber?(0)
|
||||||
#expect(search.accessibilityLabel() == "English text to translate into Japanese")
|
#expect(search.accessibilityLabel() == "English text to translate into Japanese")
|
||||||
_ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.cancelOperation(_:)))
|
_ = launcher.control(search, textView: NSTextView(), doCommandBy: #selector(NSResponder.cancelOperation(_:)))
|
||||||
|
|||||||
Reference in New Issue
Block a user