Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/ghostty.h
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,7 @@ typedef enum {
GHOSTTY_ACTION_TOGGLE_WINDOW_DECORATIONS,
GHOSTTY_ACTION_TOGGLE_QUICK_TERMINAL,
GHOSTTY_ACTION_TOGGLE_COMMAND_PALETTE,
GHOSTTY_ACTION_TOGGLE_JUMP_PALETTE,
GHOSTTY_ACTION_TOGGLE_VISIBILITY,
GHOSTTY_ACTION_TOGGLE_BACKGROUND_OPACITY,
GHOSTTY_ACTION_MOVE_TAB,
Expand Down
10 changes: 7 additions & 3 deletions macos/Sources/Features/Command Palette/CommandPalette.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct CommandOption: Identifiable, Hashable {

struct CommandPaletteView: View {
@Binding var isPresented: Bool
@Binding var isJumpPalette: Bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, doesn't have to be a binding, and just make it placeholder: String and let downstream callers set it.

var backgroundColor: Color = Color(nsColor: .windowBackgroundColor)
var options: [CommandOption]
@State private var query = ""
Expand Down Expand Up @@ -105,7 +106,7 @@ struct CommandPaletteView: View {
}

VStack(alignment: .leading, spacing: 0) {
CommandPaletteQuery(query: $query, isTextFieldFocused: _isTextFieldFocused) { event in
CommandPaletteQuery(isJumpPalette: $isJumpPalette, query: $query, isTextFieldFocused: _isTextFieldFocused) { event in
switch (event) {
case .exit:
isPresented = false
Expand Down Expand Up @@ -230,11 +231,13 @@ struct CommandPaletteView: View {

/// The text field for building the query for the command palette.
fileprivate struct CommandPaletteQuery: View {
@Binding var isJumpPalette: Bool
@Binding var query: String
var onEvent: ((KeyboardEvent) -> Void)? = nil
@FocusState private var isTextFieldFocused: Bool

init(query: Binding<String>, isTextFieldFocused: FocusState<Bool>, onEvent: ((KeyboardEvent) -> Void)? = nil) {
init(isJumpPalette: Binding<Bool>, query: Binding<String>, isTextFieldFocused: FocusState<Bool>, onEvent: ((KeyboardEvent) -> Void)? = nil) {
_isJumpPalette = isJumpPalette
_query = query
self.onEvent = onEvent
_isTextFieldFocused = isTextFieldFocused
Expand Down Expand Up @@ -266,7 +269,8 @@ fileprivate struct CommandPaletteQuery: View {
.frame(width: 0, height: 0)
.accessibilityHidden(true)

TextField("Execute a command…", text: $query)
let msg = isJumpPalette ? "Search for a session…" : "Execute a command…"
TextField(msg, text: $query)
.padding()
.font(.system(size: 20, weight: .light))
.frame(height: 48)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ struct TerminalCommandPaletteView: View {
/// result in the view disappearing.
@Binding var isPresented: Bool

/// Set this to true for the jump palette mode (only focus action).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just change this to an enum of mode (two values). And it doesn't have to be a @Binding since its only one-way.

@Binding var isJumpPalette: Bool

/// The configuration so we can lookup keyboard shortcuts.
@ObservedObject var ghosttyConfig: Ghostty.Config

Expand All @@ -30,6 +33,7 @@ struct TerminalCommandPaletteView: View {

CommandPaletteView(
isPresented: $isPresented,
isJumpPalette: $isJumpPalette,
backgroundColor: ghosttyConfig.backgroundColor,
options: commandOptions
)
Expand Down Expand Up @@ -58,13 +62,18 @@ struct TerminalCommandPaletteView: View {
/// All commands available in the command palette, combining update and terminal options.
private var commandOptions: [CommandOption] {
var options: [CommandOption] = []
// Updates always appear first
options.append(contentsOf: updateOptions)


if !isJumpPalette {
// Updates always appear first
options.append(contentsOf: updateOptions)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this to a switch on mode


let restOptions = isJumpPalette ? jumpOptions : jumpOptions + terminalOptions;

// Sort the rest. We replace ":" with a character that sorts before space
// so that "Foo:" sorts before "Foo Bar:". Use sortKey as a tie-breaker
// for stable ordering when titles are equal.
options.append(contentsOf: (jumpOptions + terminalOptions).sorted { a, b in
options.append(contentsOf: restOptions.sorted { a, b in
let aNormalized = a.title.replacingOccurrences(of: ":", with: "\t")
let bNormalized = b.title.replacingOccurrences(of: ":", with: "\t")
let comparison = aNormalized.localizedCaseInsensitiveCompare(bNormalized)
Expand Down Expand Up @@ -154,7 +163,7 @@ struct TerminalCommandPaletteView: View {
}

return CommandOption(
title: "Focus: \(displayTitle)",
title: isJumpPalette ? displayTitle : "Focus: \(displayTitle)",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this completely and change it so that in command palette mode we run a map over it which modifies the title to add a prefix.

subtitle: subtitle,
leadingIcon: "rectangle.on.rectangle",
leadingColor: displayColor?.displayColor.map { Color($0) },
Expand Down
20 changes: 19 additions & 1 deletion macos/Sources/Features/Terminal/BaseTerminalController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ class BaseTerminalController: NSWindowController,

/// This can be set to show/hide the command palette.
@Published var commandPaletteIsShowing: Bool = false

@Published var commandPaletteIsJumpPalette: Bool = false

/// Set if the terminal view should show the update overlay.
@Published var updateOverlayIsVisible: Bool = false

Expand Down Expand Up @@ -158,6 +159,11 @@ class BaseTerminalController: NSWindowController,
selector: #selector(ghosttyCommandPaletteDidToggle(_:)),
name: .ghosttyCommandPaletteDidToggle,
object: nil)
center.addObserver(
self,
selector: #selector(ghosttyJumpPaletteDidToggle(_:)),
name: .ghosttyJumpPaletteDidToggle,
object: nil)
center.addObserver(
self,
selector: #selector(ghosttyMaximizeDidToggle(_:)),
Expand Down Expand Up @@ -563,6 +569,12 @@ class BaseTerminalController: NSWindowController,
toggleCommandPalette(nil)
}

@objc private func ghosttyJumpPaletteDidToggle(_ notification: Notification) {
guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return }
guard surfaceTree.contains(surfaceView) else { return }
toggleJumpPalette(nil)
}

@objc private func ghosttyMaximizeDidToggle(_ notification: Notification) {
guard let window else { return }
guard let surfaceView = notification.object as? Ghostty.SurfaceView else { return }
Expand Down Expand Up @@ -1221,6 +1233,12 @@ class BaseTerminalController: NSWindowController,
}

@IBAction func toggleCommandPalette(_ sender: Any?) {
commandPaletteIsJumpPalette = false
commandPaletteIsShowing.toggle()
}

@IBAction func toggleJumpPalette( _ sender: Any?) {
commandPaletteIsJumpPalette = true
commandPaletteIsShowing.toggle()
}

Expand Down
6 changes: 5 additions & 1 deletion macos/Sources/Features/Terminal/TerminalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ protocol TerminalViewModel: ObservableObject {

/// The command palette state.
var commandPaletteIsShowing: Bool { get set }

/// The command palette is reused for the focus palette, this indicates what
/// mode it should be in, true for the focus palette.
var commandPaletteIsJumpPalette: Bool { get set }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace commandPaletteIsShowing with commandPaletteState and CommandPaletteState is an enum with 3 values: hidden, command, jump.


/// The update overlay should be visible.
var updateOverlayIsVisible: Bool { get }
}
Expand Down Expand Up @@ -110,6 +113,7 @@ struct TerminalView<ViewModel: TerminalViewModel>: View {
TerminalCommandPaletteView(
surfaceView: surfaceView,
isPresented: $viewModel.commandPaletteIsShowing,
isJumpPalette: $viewModel.commandPaletteIsJumpPalette,
ghosttyConfig: ghostty.config,
updateViewModel: (NSApp.delegate as? AppDelegate)?.updateViewModel) { action in
self.delegate?.performAction(action, on: surfaceView)
Expand Down
12 changes: 8 additions & 4 deletions macos/Sources/Ghostty/Ghostty.App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,10 @@ extension Ghostty {
rendererHealth(app, target: target, v: action.action.renderer_health)

case GHOSTTY_ACTION_TOGGLE_COMMAND_PALETTE:
toggleCommandPalette(app, target: target)
toggleCommandOrJumpPalette(app, target: target, jumpPalette: false)

case GHOSTTY_ACTION_TOGGLE_JUMP_PALETTE:
toggleCommandOrJumpPalette(app, target: target, jumpPalette: true)

case GHOSTTY_ACTION_TOGGLE_MAXIMIZE:
toggleMaximize(app, target: target)
Expand Down Expand Up @@ -967,9 +970,10 @@ extension Ghostty {
}
}

private static func toggleCommandPalette(
private static func toggleCommandOrJumpPalette(
_ app: ghostty_app_t,
target: ghostty_target_s) {
target: ghostty_target_s,
jumpPalette: Bool) {
switch (target.tag) {
case GHOSTTY_TARGET_APP:
Ghostty.logger.warning("toggle command palette does nothing with an app target")
Expand All @@ -979,7 +983,7 @@ extension Ghostty {
guard let surface = target.target.surface else { return }
guard let surfaceView = self.surfaceView(from: surface) else { return }
NotificationCenter.default.post(
name: .ghosttyCommandPaletteDidToggle,
name: jumpPalette ? .ghosttyJumpPaletteDidToggle : .ghosttyCommandPaletteDidToggle,
object: surfaceView
)

Expand Down
1 change: 1 addition & 0 deletions macos/Sources/Ghostty/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ extension Notification.Name {
static let ghosttyDidChangeReadonly = Notification.Name("com.mitchellh.ghostty.didChangeReadonly")
static let ReadonlyKey = ghosttyDidChangeReadonly.rawValue + ".readonly"
static let ghosttyCommandPaletteDidToggle = Notification.Name("com.mitchellh.ghostty.commandPaletteDidToggle")
static let ghosttyJumpPaletteDidToggle = Notification.Name("com.mitchellh.ghostty.jumpPaletteDidToggle")

/// Toggle maximize of current window
static let ghosttyMaximizeDidToggle = Notification.Name("com.mitchellh.ghostty.maximizeDidToggle")
Expand Down
6 changes: 6 additions & 0 deletions src/Surface.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5524,6 +5524,12 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
{},
),

.toggle_jump_palette => return try self.rt_app.performAction(
.{ .surface = self },
.toggle_jump_palette,
{},
),

.toggle_background_opacity => return try self.rt_app.performAction(
.{ .surface = self },
.toggle_background_opacity,
Expand Down
4 changes: 4 additions & 0 deletions src/apprt/action.zig
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ pub const Action = union(Key) {
/// Toggle the command palette. This currently only works on macOS.
toggle_command_palette,

/// Toggle the jump palette. This currently only works on macOS.
toggle_jump_palette,

/// Toggle the visibility of all Ghostty terminal windows.
toggle_visibility,

Expand Down Expand Up @@ -339,6 +342,7 @@ pub const Action = union(Key) {
toggle_window_decorations,
toggle_quick_terminal,
toggle_command_palette,
toggle_jump_palette,
toggle_visibility,
toggle_background_opacity,
move_tab,
Expand Down
7 changes: 7 additions & 0 deletions src/input/Binding.zig
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,12 @@ pub const Action = union(enum) {
/// version can be found by running `ghostty +version`.
toggle_command_palette,

/// Toggle the jump palette.
///
/// The jump palette is a popup like the command palette but restricted
/// to actions focusing a session.
toggle_jump_palette,

/// Toggle the quick terminal.
///
/// The quick terminal, also known as the "Quake-style" or drop-down
Expand Down Expand Up @@ -1250,6 +1256,7 @@ pub const Action = union(enum) {
.toggle_secure_input,
.toggle_mouse_reporting,
.toggle_command_palette,
.toggle_jump_palette,
.toggle_background_opacity,
.show_on_screen_keyboard,
.reset_window_size,
Expand Down
1 change: 1 addition & 0 deletions src/input/command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ fn actionCommands(action: Action.Key) []const Command {
// No commands because I'm not sure they make sense in a command
// palette context.
.toggle_command_palette,
.toggle_jump_palette,
.toggle_quick_terminal,
.toggle_visibility,
.previous_tab,
Expand Down