using Sdt.Config;
using Sdt.Runner;
using Spectre.Console;
namespace Sdt.Tui;
/// Thin wrapper used in Spectre.Console selection prompts.
internal sealed record MenuItem(string Display, string Value);
public enum AppExitReason { Quit, SwitchProject }
public sealed record AppResult(AppExitReason Reason, string? NewProjectRoot = null);
public sealed class App
{
private DevToolConfig _config;
private string _projectRoot;
private readonly WorkspaceConfig? _workspace;
private readonly string? _workspaceRoot;
private IReadOnlyDictionary TargetMap =>
_config.Targets.ToDictionary(t => t.Id, StringComparer.OrdinalIgnoreCase);
public App(
DevToolConfig config,
string projectRoot,
WorkspaceConfig? workspace = null,
string? workspaceRoot = null)
{
_config = config;
_projectRoot = projectRoot;
_workspace = workspace;
_workspaceRoot = workspaceRoot;
}
public async Task RunAsync()
{
while (true)
{
AnsiConsole.Clear();
RenderBanner();
var choice = ShowMainMenu();
switch (choice)
{
case "__env__":
EditEnvironment();
break;
case "__toolchains__":
await new ToolchainScreen(_config, _projectRoot).RunAsync();
break;
case "__workspace__":
if (_workspace is not null && _workspaceRoot is not null)
{
var switcher = new WorkspaceScreen(_workspace, _workspaceRoot, _projectRoot);
var newRoot = switcher.SelectProject();
if (newRoot is not null)
return new AppResult(AppExitReason.SwitchProject, newRoot);
}
break;
case "__quit__":
AnsiConsole.MarkupLine("\n" + Theme.Faint("Later.") + "\n");
return new AppResult(AppExitReason.Quit);
default:
var targetMap = TargetMap;
if (targetMap.TryGetValue(choice, out var target))
await RunTargetAsync(target, targetMap);
break;
}
if (choice != "__quit__")
{
AnsiConsole.MarkupLine("\n" + Theme.Faint("Press any key to return to the menu..."));
Console.ReadKey(intercept: true);
}
}
}
// ── Banner ────────────────────────────────────────────────────────────────
private void RenderBanner()
{
// Phosphor green figlet
AnsiConsole.Write(new FigletText("SDT").Color(Theme.GreenColor));
// Project + workspace info line
var wsInfo = _workspace is not null
? $" [{Theme.GreenDim}]∙ {Markup.Escape(_workspace.Name)}[/]"
: string.Empty;
AnsiConsole.Write(
new Rule($"[bold {Theme.GreenBold}]{Markup.Escape(_config.Name)}[/] [{Theme.GreenDim}]v{Markup.Escape(_config.Version)}[/]{wsInfo}")
.RuleStyle(Theme.DimStyle));
AnsiConsole.MarkupLine(Theme.Faint($"root: {_projectRoot}") + "\n");
}
// ── Main menu ─────────────────────────────────────────────────────────────
private string ShowMainMenu()
{
var prompt = new SelectionPrompt