Adds a 'New session' header button in Mission Control that opens a folder picker and starts a task-less embedded ConPTY session in the chosen directory. ConPtyPaneViewModel TaskId is now nullable (ad-hoc panes have no task and are never deduped) with a CreateAdHoc factory; the view does the picking, the VM stays picker-agnostic.
69 lines
2.4 KiB
C#
69 lines
2.4 KiB
C#
using System.Linq;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Input;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Platform.Storage;
|
|
using Avalonia.VisualTree;
|
|
using ClaudeDo.Ui.ViewModels;
|
|
using ClaudeDo.Ui.ViewModels.Islands;
|
|
|
|
namespace ClaudeDo.Ui.Views.MissionControl;
|
|
|
|
public partial class MissionControlView : UserControl
|
|
{
|
|
// Shared with MonitorPaneView (the drag source).
|
|
public static readonly DataFormat<string> PaneFormat =
|
|
DataFormat.CreateStringApplicationFormat("claudedo-monitor-pane");
|
|
|
|
public MissionControlView()
|
|
{
|
|
InitializeComponent();
|
|
AddHandler(DragDrop.DragOverEvent, OnPaneDragOver);
|
|
AddHandler(DragDrop.DropEvent, OnPaneDrop);
|
|
}
|
|
|
|
// Ad-hoc ConPTY session: the view owns the folder picker, the VM only takes the chosen path.
|
|
private async void OnNewSessionClicked(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (DataContext is not MissionControlViewModel vm) return;
|
|
var topLevel = TopLevel.GetTopLevel(this);
|
|
if (topLevel is null) return;
|
|
|
|
var folders = await topLevel.StorageProvider.OpenFolderPickerAsync(new FolderPickerOpenOptions
|
|
{
|
|
Title = "Choose a directory",
|
|
AllowMultiple = false,
|
|
});
|
|
if (folders.Count == 0) return;
|
|
|
|
await vm.OpenAdHocConPtySessionAsync(folders[0].Path.LocalPath);
|
|
}
|
|
|
|
private void OnPaneDragOver(object? sender, DragEventArgs e)
|
|
{
|
|
var dt = e.DataTransfer;
|
|
e.DragEffects = (dt?.Contains(PaneFormat) ?? false) ? DragDropEffects.Move : DragDropEffects.None;
|
|
}
|
|
|
|
private void OnPaneDrop(object? sender, DragEventArgs e)
|
|
{
|
|
if (DataContext is not MissionControlViewModel vm) return;
|
|
var dt = e.DataTransfer;
|
|
if (dt is null) return;
|
|
|
|
// A pane dragged within the grid → reorder. (Drag-to-queue from the main app now arrives
|
|
// via the custom ghost drag's screen hit-test, not an OLE drop.)
|
|
var draggedId = dt.TryGetValue(PaneFormat);
|
|
if (string.IsNullOrEmpty(draggedId)) return;
|
|
if (e.Source is not Avalonia.Visual src) return;
|
|
|
|
var targetPane = src.FindAncestorOfType<MonitorPaneView>();
|
|
if (targetPane?.DataContext is not TaskMonitorViewModel target) return;
|
|
|
|
var dragged = vm.Monitors.FirstOrDefault(m => m.SubscribedTaskId == draggedId);
|
|
if (dragged is null) return;
|
|
|
|
vm.MoveMonitor(dragged, target);
|
|
}
|
|
}
|