Skip to content

Commit c879e7a

Browse files
committed
Replace typed home action entry with menu navigation
1 parent 05650f8 commit c879e7a

3 files changed

Lines changed: 38 additions & 21 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ Treat explicit frustration, swearing, sarcasm, repeated rejection, or "don't do
109109
- In the interactive shell home screen, keep `Update all skills` visible as a first-class action even when no outdated skills are currently detected. In the zero-outdated case, keep the action callable and return a clear no-op status instead of hiding the entry.
110110
- In the interactive shell, expose a first-class direct `Skills` browse/install surface for people who want to inspect or pick individual skills without going through `Collections` or `Bundles` first. Collection- and bundle-first flows may stay primary for taxonomy and grouped installs, but direct individual-skill browsing cannot be missing.
111111
- Keep the public site IA and the interactive shell IA synchronized. If the site exposes a first-class surface such as `Packages`, `Bundles`, `Collections`, `Skills`, `Agents`, or `About`, the CLI must either expose the same surface explicitly or make the intentional difference obvious in the home UI and help copy. Prefer deriving site and CLI primary-navigation labels from one shared repo-owned model instead of maintaining parallel hardcoded lists.
112+
- The interactive shell home screen must be navigable as a real menu without requiring typed action keys. Do not make `Action key:`-style manual key entry the primary way to leave the home screen; use arrow/enter menu navigation and keep any shortcut hints strictly secondary.
112113
- When replacing the interactive shell home or entry UX, remove the superseded legacy menu flow from the published tool instead of leaving a parallel flat prompt path behind the new control center.
113114
- In the interactive shell, escape Spectre markup syntax in every prompt choice label and prompt-only caption before rendering selection or multi-selection pickers. Keep a regression test that covers labels containing brackets and `.NET` text from bundle or collection surfaces.
114115
- Interactive collection, analysis, bundle, installed, and agent pages must not ship as giant spreadsheet-style matrices. Each page needs an intentional dominant pane plus supporting card-style summaries; use a large table only when it is clearly the best representation for that page instead of the default.

cli/ManagedCode.DotnetSkills/InteractiveConsoleApp.cs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ private void RenderDashboard()
279279

280280
var controlLines = new List<Spectre.Console.Rendering.IRenderable>
281281
{
282-
new Spectre.Console.Markup("[bold grey]1-9 / U / R / W[/] [dim]choose surface[/] [bold grey]0[/] [dim]exit[/] [bold grey]Space[/] [dim]multi-select[/]"),
282+
new Spectre.Console.Markup("[bold grey]Menu[/] [dim]uses arrow keys and Enter[/] [bold grey]Space[/] [dim]multi-select[/]"),
283283
new Spectre.Console.Markup("[dim]Packages map NuGet ids -> skills[/] [dim]Collections narrow before install[/] [dim]Bundles stay focused[/]"),
284284
new Spectre.Console.Markup("[dim]Install preview stays mandatory before writes[/]"),
285285
};
@@ -501,7 +501,6 @@ private static Spectre.Console.Grid BuildRichTwoColumn(
501501
private static Spectre.Console.Table BuildRichNavigationTable(IReadOnlyList<HomeActionView> homeActions)
502502
{
503503
var table = new Spectre.Console.Table().Border(Spectre.Console.TableBorder.None).Expand();
504-
table.AddColumn("Key");
505504
table.AddColumn("Group");
506505
table.AddColumn("Area");
507506
table.AddColumn("Use");
@@ -510,7 +509,6 @@ private static Spectre.Console.Table BuildRichNavigationTable(IReadOnlyList<Home
510509
foreach (var action in homeActions)
511510
{
512511
table.AddRow(
513-
$"[bold grey]{Escape(action.HotKey.ToString())}[/]",
514512
$"[grey]{Escape(action.Section)}[/]",
515513
$"[{action.Accent}]{Escape(action.Label)}[/]",
516514
$"[dim]{Escape(action.Summary)}[/]",
@@ -533,7 +531,7 @@ private static Spectre.Console.Grid BuildRichNavigationList(IReadOnlyList<HomeAc
533531
grid.AddRow(new Spectre.Console.Markup($"[bold grey]{Escape(currentSection)}[/]"));
534532
}
535533

536-
grid.AddRow(new Spectre.Console.Markup($"[bold grey]{Escape(action.HotKey.ToString())}[/] [{action.Accent}]{Escape(action.Label)}[/] [dim]{Escape(action.Summary)}[/]"));
534+
grid.AddRow(new Spectre.Console.Markup($"[{action.Accent}]{Escape(action.Label)}[/] [dim]{Escape(action.Summary)}[/]"));
537535
grid.AddRow(new Spectre.Console.Markup($"[grey]{Escape(action.Command)}[/]"));
538536
}
539537

@@ -3557,29 +3555,36 @@ public HomeActionView SelectHomeAction(IReadOnlyList<HomeActionView> choices)
35573555
}
35583556

35593557
EnsureRichConsoleAvailable();
3558+
var prompt = new Spectre.Console.SelectionPrompt<string>
3559+
{
3560+
Title = "[deepskyblue1]Menu[/] [dim](use arrows and Enter)[/]",
3561+
PageSize = Math.Min(Math.Max(choices.Count, 8), 18),
3562+
HighlightStyle = new Spectre.Console.Style(foreground: Spectre.Console.Color.Aqua),
3563+
};
35603564

3561-
AnsiConsole.Write(new Spectre.Console.Markup("[grey]Action key[/]: "));
3562-
while (true)
3565+
var labelMap = new Dictionary<string, HomeActionView>(StringComparer.Ordinal);
3566+
foreach (var section in choices
3567+
.GroupBy(choice => choice.Section, StringComparer.Ordinal)
3568+
.OrderBy(group => group.Key, StringComparer.Ordinal))
35633569
{
3564-
var key = Console.ReadKey(intercept: true);
3565-
var keyChar = char.ToUpperInvariant(key.KeyChar);
3566-
var selected = choices.FirstOrDefault(choice => char.ToUpperInvariant(choice.HotKey) == keyChar);
3567-
if (selected is not null)
3570+
var sectionChoices = new List<string>();
3571+
foreach (var choice in section)
35683572
{
3569-
AnsiConsole.MarkupLine($"[dim]{EscapeMarkup(selected.Label)}[/]");
3570-
return selected;
3573+
var label = BuildPromptDisplayLabel(BuildHomeActionMenuLabel(choice));
3574+
labelMap[label] = choice;
3575+
sectionChoices.Add(label);
35713576
}
35723577

3573-
if (key.Key == ConsoleKey.Escape)
3574-
{
3575-
var exitAction = choices.FirstOrDefault(choice => choice.Action == HomeAction.Exit);
3576-
if (exitAction is not null)
3577-
{
3578-
AnsiConsole.MarkupLine("[dim]Exit[/]");
3579-
return exitAction;
3580-
}
3581-
}
3578+
prompt.AddChoiceGroup(EscapeMarkup(section.Key), sectionChoices);
35823579
}
3580+
3581+
var selectedLabel = SpectreConsole.Prompt(prompt);
3582+
if (labelMap.TryGetValue(selectedLabel, out var selected))
3583+
{
3584+
return selected;
3585+
}
3586+
3587+
throw new InvalidOperationException("Could not resolve the selected home action.");
35833588
}
35843589

35853590
public T Select<T>(string title, IReadOnlyList<T> choices, Func<T, string> formatter) where T : notnull
@@ -3699,6 +3704,11 @@ internal static string BuildPromptDisplayLabel(string value)
36993704
{
37003705
return EscapeMarkup(value);
37013706
}
3707+
3708+
internal static string BuildHomeActionMenuLabel(HomeActionView action)
3709+
{
3710+
return $"{action.Label} - {action.Summary}";
3711+
}
37023712
}
37033713

37043714
internal sealed class InteractiveSessionState

docs/cli-rewrite-plan.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,12 @@ Suggested accents:
459459
- [x] Reorder and recopy the home/control-center navigation so `Packages`, `Bundles`, `Collections`, `Skills`, and `Agents` stay visibly aligned with the site.
460460
- [x] Revalidate the new shared IA in a real TTY and in generated GitHub Pages output before release.
461461

462+
### Phase 3e — Home menu interaction follow-up
463+
464+
- [x] Replace typed `Action key:` home entry with a real arrow/enter menu prompt.
465+
- [x] Demote shortcut keys from the home control copy so the primary affordance reads like menu navigation instead of manual command entry.
466+
- [x] Revalidate the new home menu in a real TTY after installing the freshly packed tool locally.
467+
462468
### Phase 4 — Remove terminology drift
463469

464470
- [x] Replace remaining public `Stack` wording with `Collection` in:

0 commit comments

Comments
 (0)