feat(mission-control): custom tiles — open any console program as a tile
Programs are configured in Settings → Custom tiles and stored in ui.config.json. A tile with a fixed folder starts on click; without one it offers a submenu of home, the list folders and browse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
43b03e4837
commit
f37b59d58f
@@ -321,7 +321,7 @@ public class MissionControlViewModelTests : IDisposable
|
||||
Assert.Equal(4, vm.SessionTargets.Count); // home, browse, Alpha, VPS
|
||||
Assert.Contains("VPS", vm.SessionTargets[3].Label);
|
||||
|
||||
vm.SessionTargets[3].StartCommand.Execute(null);
|
||||
vm.SessionTargets[3].StartCommand!.Execute(null);
|
||||
var pane = Assert.Single(vm.ConPtySessions);
|
||||
Assert.Equal("VPS", pane.DisplayTitle);
|
||||
}
|
||||
@@ -339,7 +339,7 @@ public class MissionControlViewModelTests : IDisposable
|
||||
|
||||
using var vm = BuildVm(new FakeWorker());
|
||||
var browsed = false;
|
||||
vm.BrowseForSessionRequested = () => browsed = true;
|
||||
vm.PickFolderAsync = () => { browsed = true; return Task.FromResult<string?>(null); };
|
||||
|
||||
await vm.RefreshSessionTargetsAsync();
|
||||
|
||||
@@ -347,10 +347,65 @@ public class MissionControlViewModelTests : IDisposable
|
||||
Assert.Equal(4, vm.SessionTargets.Count);
|
||||
Assert.Equal(new[] { "Beta", "Alpha" }, vm.SessionTargets.Skip(2).Select(t => t.Label).ToArray());
|
||||
|
||||
vm.SessionTargets[1].StartCommand.Execute(null);
|
||||
vm.SessionTargets[1].StartCommand!.Execute(null);
|
||||
Assert.True(browsed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RefreshSessionTargetsAsync_CustomTiles_FixedFolderStartsDirectly_EmptyFolderOffersFolderSubmenu()
|
||||
{
|
||||
await using (var db = NewContext())
|
||||
{
|
||||
db.Lists.Add(new ListEntity { Id = "L1", Name = "Alpha", WorkingDir = @"C:\repos\alpha", CreatedAt = DateTime.UtcNow });
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
var settings = new AppSettings { ConfigPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("n") + ".json") };
|
||||
settings.CustomTiles.Add(new CustomTile { Name = "Shell", Program = "cmd", Folder = @"C:\" });
|
||||
settings.CustomTiles.Add(new CustomTile { Name = "Git", Program = "cmd" });
|
||||
var worker = new FakeWorker();
|
||||
using var vm = new MissionControlViewModel(new TestDbFactory(NewContext), worker, new UsagePillViewModel(worker), settings);
|
||||
|
||||
await vm.RefreshSessionTargetsAsync();
|
||||
|
||||
// Home, browse, Alpha, then the two custom tiles.
|
||||
var fixedTile = vm.SessionTargets[3];
|
||||
var askTile = vm.SessionTargets[4];
|
||||
Assert.NotNull(fixedTile.StartCommand);
|
||||
Assert.Null(fixedTile.Children);
|
||||
Assert.Null(askTile.StartCommand);
|
||||
// Same folder choice as the top level: home, the list folders, then browse.
|
||||
Assert.Equal(3, askTile.Children!.Count);
|
||||
Assert.Equal("Alpha", askTile.Children[1].Label);
|
||||
|
||||
askTile.Children[1].StartCommand!.Execute(null);
|
||||
var pane = Assert.Single(vm.ConPtySessions);
|
||||
Assert.Equal("Git", pane.DisplayTitle);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CustomTile_SplitArgs_SplitsOnWhitespace_QuotesGroup()
|
||||
{
|
||||
Assert.Equal(new[] { "lenovo-server", "-p", "56789" }, CustomTile.SplitArgs("lenovo-server -p 56789 "));
|
||||
Assert.Equal(new[] { "-c", "git log --oneline", "" }, CustomTile.SplitArgs("-c \"git log --oneline\" \"\""));
|
||||
Assert.Empty(CustomTile.SplitArgs(null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AppSettings_CustomTiles_RoundTripThroughConfigFile()
|
||||
{
|
||||
var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("n") + ".json");
|
||||
try
|
||||
{
|
||||
var settings = new AppSettings { ConfigPath = path };
|
||||
settings.CustomTiles.Add(new CustomTile { Name = "Server", Program = "ssh", Arguments = "lenovo-server" });
|
||||
settings.Save();
|
||||
|
||||
var tile = Assert.Single(AppSettings.Load(path).CustomTiles);
|
||||
Assert.Equal(("Server", "ssh", "lenovo-server", ""), (tile.Name, tile.Program, tile.Arguments, tile.Folder));
|
||||
}
|
||||
finally { File.Delete(path); }
|
||||
}
|
||||
|
||||
private async Task SeedQueueAsync()
|
||||
{
|
||||
await using var db = NewContext();
|
||||
|
||||
Reference in New Issue
Block a user