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 { ZStack(alignment: .topLeading) { GhosttyTerminalView( terminalSurface: panel.surface, isActive: isFocused, isVisibleInUI: isVisibleInUI, portalZPriority: portalPriority, showsInactiveOverlay: isSplit && !isFocused, showsUnreadNotificationRing: hasUnreadNotification, inactiveOverlayColor: appearance.unfocusedOverlayNSColor, inactiveOverlayOpacity: appearance.unfocusedOverlayOpacity, 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) // Search overlay if let searchState = panel.searchState { SurfaceSearchOverlay( surface: panel.surface, searchState: searchState, onClose: { panel.searchState = nil panel.hostedView.moveFocus() } ) } } } } /// 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 ) } }