import SwiftUI import Foundation import AppKit /// View for rendering a terminal panel struct TerminalPanelView: View { @ObservedObject var panel: TerminalPanel let isFocused: Bool let isVisibleInUI: Bool let portalPriority: Int let isSplit: Bool let appearance: PanelAppearance let hasUnreadNotification: Bool let onFocus: () -> Void let onTriggerFlash: () -> Void var body: some View { // Layering contract: terminal find UI is mounted in GhosttySurfaceScrollView (AppKit portal layer) // via `searchState`. Rendering `SurfaceSearchOverlay` in this SwiftUI container can hide it. GhosttyTerminalView( terminalSurface: panel.surface, isActive: isFocused, isVisibleInUI: isVisibleInUI, portalZPriority: portalPriority, showsInactiveOverlay: isSplit && !isFocused, showsUnreadNotificationRing: hasUnreadNotification, inactiveOverlayColor: appearance.unfocusedOverlayNSColor, inactiveOverlayOpacity: appearance.unfocusedOverlayOpacity, searchState: panel.searchState, reattachToken: panel.viewReattachToken, onFocus: { _ in onFocus() }, onTriggerFlash: onTriggerFlash ) // Keep the NSViewRepresentable identity stable across bonsplit structural updates. // This prevents transient teardown/recreate that can momentarily detach the hosted terminal view. .id(panel.id) .background(Color.clear) } } /// Shared appearance settings for panels struct PanelAppearance { let dividerColor: Color let unfocusedOverlayNSColor: NSColor let unfocusedOverlayOpacity: Double static func fromConfig(_ config: GhosttyConfig) -> PanelAppearance { PanelAppearance( dividerColor: Color(nsColor: config.resolvedSplitDividerColor), unfocusedOverlayNSColor: config.unfocusedSplitOverlayFill, unfocusedOverlayOpacity: config.unfocusedSplitOverlayOpacity ) } }