macos: implement prompt_tab_title

pull/9879/head
Mitchell Hashimoto 2025-12-11 16:00:13 -08:00
parent 32033c9e1a
commit e93a4a911f
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
2 changed files with 75 additions and 3 deletions

View File

@ -81,6 +81,15 @@ class BaseTerminalController: NSWindowController,
/// The cancellables related to our focused surface.
private var focusedSurfaceCancellables: Set<AnyCancellable> = []
/// An override title for the tab/window set by the user via prompt_tab_title.
/// When set, this takes precedence over the computed title from the terminal.
var tabTitleOverride: String? = nil {
didSet { applyTitleToWindow() }
}
/// The last computed title from the focused surface (without the override).
private var lastComputedTitle: String = "👻"
/// The time that undo/redo operations that contain running ptys are valid for.
var undoExpiration: Duration {
ghostty.config.undoTimeout
@ -325,6 +334,37 @@ class BaseTerminalController: NSWindowController,
self.alert = alert
}
/// Prompt the user to change the tab/window title.
func promptTabTitle() {
guard let window else { return }
let alert = NSAlert()
alert.messageText = "Change Tab Title"
alert.informativeText = "Leave blank to restore the default."
alert.alertStyle = .informational
let textField = NSTextField(frame: NSRect(x: 0, y: 0, width: 250, height: 24))
textField.stringValue = tabTitleOverride ?? window.title
alert.accessoryView = textField
alert.addButton(withTitle: "OK")
alert.addButton(withTitle: "Cancel")
alert.window.initialFirstResponder = textField
alert.beginSheetModal(for: window) { [weak self] response in
guard let self else { return }
guard response == .alertFirstButtonReturn else { return }
let newTitle = textField.stringValue
if newTitle.isEmpty {
self.tabTitleOverride = nil
} else {
self.tabTitleOverride = newTitle
}
}
}
/// Close a surface from a view.
func closeSurface(
_ view: Ghostty.SurfaceView,
@ -718,10 +758,13 @@ class BaseTerminalController: NSWindowController,
}
private func titleDidChange(to: String) {
lastComputedTitle = to
applyTitleToWindow()
}
private func applyTitleToWindow() {
guard let window else { return }
// Set the main window title
window.title = to
window.title = tabTitleOverride ?? lastComputedTitle
}
func pwdDidChange(to: URL?) {

View File

@ -525,6 +525,9 @@ extension Ghostty {
case GHOSTTY_ACTION_PROMPT_TITLE:
return promptTitle(app, target: target)
case GHOSTTY_ACTION_PROMPT_TAB_TITLE:
return promptTabTitle(app, target: target)
case GHOSTTY_ACTION_PWD:
pwdChanged(app, target: target, v: action.action.pwd)
@ -1368,6 +1371,32 @@ extension Ghostty {
return true
}
private static func promptTabTitle(
_ app: ghostty_app_t,
target: ghostty_target_s) -> Bool {
switch (target.tag) {
case GHOSTTY_TARGET_APP:
guard let window = NSApp.mainWindow ?? NSApp.keyWindow,
let controller = window.windowController as? BaseTerminalController
else { return false }
controller.promptTabTitle()
return true
case GHOSTTY_TARGET_SURFACE:
guard let surface = target.target.surface else { return false }
guard let surfaceView = self.surfaceView(from: surface) else { return false }
guard let window = surfaceView.window,
let controller = window.windowController as? BaseTerminalController
else { return false }
controller.promptTabTitle()
return true
default:
assertionFailure()
return false
}
}
private static func pwdChanged(
_ app: ghostty_app_t,
target: ghostty_target_s,