mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-05-24 13:50:11 +00:00
macOS: add bottom bar when child exits
This commit is contained in:
@@ -662,8 +662,7 @@ extension Ghostty {
|
||||
case GHOSTTY_ACTION_QUIT_TIMER:
|
||||
fallthrough
|
||||
case GHOSTTY_ACTION_SHOW_CHILD_EXITED:
|
||||
Ghostty.logger.info("known but unimplemented action action=\(action.tag.rawValue)")
|
||||
return false
|
||||
return showChildExited(app, target: target, v: action.action.child_exited)
|
||||
case GHOSTTY_ACTION_COPY_TITLE_TO_CLIPBOARD:
|
||||
return copyTitleToClipboard(app, target: target)
|
||||
default:
|
||||
@@ -1632,6 +1631,25 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
private static func showChildExited(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s,
|
||||
v: ghostty_surface_message_childexited_s,
|
||||
) -> Bool {
|
||||
switch target.tag {
|
||||
case GHOSTTY_TARGET_SURFACE:
|
||||
guard let surface = target.target.surface else { return false }
|
||||
guard let surfaceView = self.surfaceView(from: surface) else { return false }
|
||||
// We handle this when the window is visible and timetime_ms is greater than 0,
|
||||
// which will rule out exit codes on launch
|
||||
guard surfaceView.window != nil, v.timetime_ms > 0 else { return false }
|
||||
surfaceView.setChildExitedMessage(.init(v))
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private static func copyTitleToClipboard(
|
||||
_ app: ghostty_app_t,
|
||||
target: ghostty_target_s) -> Bool {
|
||||
|
||||
23
macos/Sources/Ghostty/Ghostty.ChildExitedMessage.swift
Normal file
23
macos/Sources/Ghostty/Ghostty.ChildExitedMessage.swift
Normal file
@@ -0,0 +1,23 @@
|
||||
import Foundation
|
||||
import GhosttyKit
|
||||
import SwiftUI
|
||||
|
||||
extension Ghostty {
|
||||
struct ChildExitedMessage {
|
||||
enum Level {
|
||||
case success, error
|
||||
}
|
||||
let text: String
|
||||
let level: Level
|
||||
|
||||
init(_ message: ghostty_surface_message_childexited_s) {
|
||||
switch Int(message.exit_code) {
|
||||
case Int(EXIT_SUCCESS):
|
||||
level = .success
|
||||
default:
|
||||
level = .error
|
||||
}
|
||||
text = "Process exited. Press any key to close the terminal."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import SwiftUI
|
||||
|
||||
struct ChildExitedMessageBar: View {
|
||||
let msg: Ghostty.ChildExitedMessage
|
||||
@State private var isHovered: Bool = false
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 6) {
|
||||
Text(msg.text)
|
||||
.fontWeight(.medium)
|
||||
.lineLimit(1)
|
||||
.truncationMode(.tail)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.background(msg.level.backgroundStyle)
|
||||
.foregroundColor(msg.level.foregroundColor)
|
||||
.contentShape(.rect)
|
||||
.accessibilityLabel(msg.text)
|
||||
.transition(.move(edge: .bottom))
|
||||
.opacity(isHovered ? 0 : 1)
|
||||
.allowsHitTesting(false)
|
||||
.overlay {
|
||||
Color.clear
|
||||
.onHover {
|
||||
isHovered = $0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Ghostty.ChildExitedMessage.Level {
|
||||
var foregroundColor: Color {
|
||||
.primary
|
||||
}
|
||||
|
||||
var backgroundStyle: AnyShapeStyle {
|
||||
switch self {
|
||||
case .success:
|
||||
AnyShapeStyle(.background)
|
||||
case .error:
|
||||
AnyShapeStyle(.red.opacity(0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,9 @@ extension Ghostty {
|
||||
/// True when the surface should show a highlight effect (e.g., when presented via goto_split).
|
||||
@Published private(set) var highlighted: Bool = false
|
||||
|
||||
/// A message sent from `ghostty_surface_t` when a child process exited
|
||||
@Published private(set) var childExitedMessage: ChildExitedMessage?
|
||||
|
||||
var surface: ghostty_surface_t? {
|
||||
nil
|
||||
}
|
||||
@@ -92,6 +95,10 @@ extension Ghostty {
|
||||
}
|
||||
}
|
||||
|
||||
func setChildExitedMessage(_ message: ChildExitedMessage) {
|
||||
self.childExitedMessage = message
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
func focusDidChange(_ focused: Bool) {}
|
||||
|
||||
@@ -137,6 +137,12 @@ extension Ghostty {
|
||||
if let url = surfaceView.hoverUrl {
|
||||
URLHoverBanner(url: url)
|
||||
}
|
||||
|
||||
// Show a bar to indicate a child process has exited.
|
||||
if let msg = surfaceView.childExitedMessage {
|
||||
ChildExitedMessageBar(msg: msg)
|
||||
.font(.system(size: min(surfaceView.cellSize.height * 0.8, 30)))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user