using System.Runtime.CompilerServices; using Avalonia.Controls; using ClaudeDo.Ui.ViewModels.MissionControl; namespace ClaudeDo.Ui.Views.MissionControl; /// /// Hosts the single long-lived of a . /// The Mission Control layouts recreate their DataTemplate content freely (the overview grid is /// rebuilt from scratch on every pane/column change, focus-mode tabs re-present on selection), but /// the running PTY session is bound to the original TerminalControl — a freshly instantiated view /// would render a dead, empty terminal. This host therefore reparents the pane's one real view /// instead of letting the template create a new one. Safe while the session runs because /// puts the control in permanent reparent mode /// (detach never kills the child process). /// public sealed class ConPtyPaneHost : Decorator { // Keyed weakly so a closed pane's view dies with its view model. private static readonly ConditionalWeakTable Views = new(); public ConPtyPaneHost() { DataContextChanged += (_, _) => Reclaim(); AttachedToVisualTree += (_, _) => Reclaim(); } /// Takes ownership of the pane's cached view, stealing it from whichever host showed /// it last — but only while effectively visible, so the hidden layout (overview grid vs. focus /// tabs) never yanks the view away from the visible one. public void Reclaim() { var vm = DataContext as ConPtyPaneViewModel; // A recycled host now bound to a different pane must not keep showing the old view. // The cached views carry their own local DataContext, so this check can't be fooled // by DataContext inheritance. if (Child is ConPtyPaneView stale && !ReferenceEquals(stale.DataContext, vm)) Child = null; if (vm is null || !IsEffectivelyVisible) return; var view = Views.GetValue(vm, static key => new ConPtyPaneView { DataContext = key }); if (ReferenceEquals(Child, view)) return; if (view.Parent is ConPtyPaneHost previous) previous.Child = null; Child = view; } }