macOS: hook up command palette C API to actual command palette

This commit is contained in:
Mitchell Hashimoto
2025-04-19 13:44:47 -07:00
parent 8615dfb73d
commit 5fab6faf04
4 changed files with 35 additions and 4 deletions

View File

@ -76,6 +76,7 @@ struct CommandPaletteView: View {
.padding(.bottom, 4)
CommandTable(
options: options,
query: $query,
selectedIndex: $selectedIndex,
hoveredOptionID: $hoveredOptionID)
@ -197,7 +198,7 @@ fileprivate struct CommandRow: View {
var body: some View {
Button(action: option.action) {
HStack {
Text(option.title)
Text(option.title.lowercased())
Spacer()
if let shortcut = option.shortcut {
Text(shortcut)

View File

@ -44,6 +44,10 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
// An optional delegate to receive information about terminal changes.
weak var delegate: (any TerminalViewDelegate)? = nil
// The most recently focused surface, equal to focusedSurface when
// it is non-nil.
@State private var lastFocusedSurface: Weak<Ghostty.SurfaceView> = .init()
// This seems like a crutch after switching from SwiftUI to AppKit lifecycle.
@FocusState private var focused: Bool
@ -68,6 +72,25 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
return URL(fileURLWithPath: surfacePwd)
}
// The commands available to the command palette.
private var commandOptions: [CommandOption] {
guard let surface = lastFocusedSurface.value?.surface else { return [] }
var ptr: UnsafeMutablePointer<ghostty_command_s>? = nil
var count: Int = 0
ghostty_surface_commands(surface, &ptr, &count)
guard let ptr else { return [] }
let buffer = UnsafeBufferPointer(start: ptr, count: count)
return Array(buffer).map { c in
let action = String(cString: c.action)
return CommandOption(
title: String(cString: c.title),
shortcut: ghostty.config.keyEquivalent(for: action)?.description
) {}
}
}
@State var showingCommandPalette = false
var body: some View {
@ -100,6 +123,12 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
.focused($focused)
.onAppear { self.focused = true }
.onChange(of: focusedSurface) { newValue in
// We want to keep track of our last focused surface so even if
// we lose focus we keep this set to the last non-nil value.
if newValue != nil {
lastFocusedSurface = .init(newValue)
}
self.delegate?.focusedSurfaceDidChange(to: newValue)
}
.onChange(of: title) { newValue in
@ -133,7 +162,8 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
CommandPaletteView(
isPresented: $showingCommandPalette,
backgroundColor: ghostty.config.backgroundColor
backgroundColor: ghostty.config.backgroundColor,
options: commandOptions
)
.transition(
.move(edge: .top)

View File

@ -11,7 +11,7 @@ extension Ghostty {
return Self.keyToEquivalent[key]
}
/// Return the keyboard shortcut for a trigger.
/// Return the key equivalent for the given trigger.
///
/// Returns nil if the trigger doesn't have an equivalent KeyboardShortcut. This is possible
/// because Ghostty input triggers are a superset of what can be represented by a macOS

View File

@ -3,7 +3,7 @@
class Weak<T: AnyObject> {
weak var value: T?
init(_ value: T) {
init(_ value: T? = nil) {
self.value = value
}
}