From 3e9d80fe98aae88b87cef94eb076e26b2051e40b Mon Sep 17 00:00:00 2001 From: TomiEckert Date: Wed, 20 May 2026 11:20:00 +0200 Subject: [PATCH] fix color formatting core dump --- hanatui/src/Tui/KeySelectionScreen.cs | 75 +++++++++++++++++++-------- hanatui/src/Tui/MainMenuScreen.cs | 10 ++-- hanatui/src/Tui/OperationForms.cs | 22 +++++--- 3 files changed, 74 insertions(+), 33 deletions(-) diff --git a/hanatui/src/Tui/KeySelectionScreen.cs b/hanatui/src/Tui/KeySelectionScreen.cs index cb86dcc..a8fce70 100644 --- a/hanatui/src/Tui/KeySelectionScreen.cs +++ b/hanatui/src/Tui/KeySelectionScreen.cs @@ -1,5 +1,6 @@ using HanaTui.Hana; using Spectre.Console; +using SysText = System.Text; namespace HanaTui.Tui; @@ -9,6 +10,10 @@ namespace HanaTui.Tui; /// public static class KeySelectionScreen { + // Sentinel values for the non-key choices + private const string ManualEntry = "__MANUAL__"; + private const string ExitChoice = "__EXIT__"; + public static string? Run() { AnsiConsole.Clear(); @@ -28,7 +33,7 @@ public static class KeySelectionScreen // Show client path for reference var clientDir = HdbClientLocator.ClientDirectory; if (clientDir is not null) - AnsiConsole.MarkupLine($"[dim]HDB client: {clientDir}[/]\n"); + AnsiConsole.MarkupLine($"[dim]HDB client: {Markup.Escape(clientDir)}[/]\n"); // Load keys with a spinner List keys = []; @@ -47,49 +52,77 @@ public static class KeySelectionScreen AnsiConsole.MarkupLine("You can still enter a key name manually.\n"); } - // Build selection choices - var choices = new List(); + // Build a plain-string list where the value IS the key name (or sentinel). + // We use SelectionPrompt with a display converter so Spectre renders + // the label as markup but we always get back the plain key name. + var choiceNames = new List(); + var displayMap = new Dictionary(); // name -> display label + foreach (var k in keys) { - var detail = BuildKeyDetail(k); - choices.Add(detail); + choiceNames.Add(k.Name); + displayMap[k.Name] = BuildKeyDisplay(k); } - choices.Add("[dim][ Enter key name manually ][/]"); - choices.Add("[red][ Exit ][/]"); + + choiceNames.Add(ManualEntry); + displayMap[ManualEntry] = "[ Enter key name manually ]"; + + choiceNames.Add(ExitChoice); + displayMap[ExitChoice] = "[ Exit ]"; var prompt = new SelectionPrompt() .Title("[bold]Select HDBUSERSTORE key:[/]") .PageSize(15) .HighlightStyle(Style.Parse("bold dodgerblue1")) - .AddChoices(choices); + .UseConverter(name => displayMap.TryGetValue(name, out var label) ? label : name) + .AddChoices(choiceNames); var selected = AnsiConsole.Prompt(prompt); - if (selected.Contains("Exit")) + if (selected == ExitChoice) return null; - if (selected.Contains("manually")) + if (selected == ManualEntry) { var manual = AnsiConsole.Ask("[bold]Enter HDBUSERSTORE key name:[/]").Trim(); return string.IsNullOrWhiteSpace(manual) ? null : manual; } - // Extract the key name from the formatted string (it's always the first word) - var keyName = selected.Split(' ')[0].Trim(); - return keyName; + // selected is already the plain key name + return selected; } - private static string BuildKeyDetail(HdbUserstoreKey k) + /// + /// Builds a plain-text display label (no markup) for a key. + /// Spectre's UseConverter renders the returned string as plain text. + /// + private static string BuildKeyDisplay(HdbUserstoreKey k) { - var parts = new List { k.Name }; + var sb = new SysText.StringBuilder(k.Name); if (!string.IsNullOrEmpty(k.Host)) - parts.Add($"[dim]{k.Host}:{k.Port}[/]"); - if (!string.IsNullOrEmpty(k.Tenant)) - parts.Add($"[dim]@{k.Tenant}[/]"); - if (!string.IsNullOrEmpty(k.User)) - parts.Add($"[dim]user={k.User}[/]"); + { + sb.Append(" "); + sb.Append(k.Host); + if (!string.IsNullOrEmpty(k.Port)) + { + sb.Append(':'); + sb.Append(k.Port); + } + } - return string.Join(" ", parts); + if (!string.IsNullOrEmpty(k.Tenant)) + { + sb.Append('@'); + sb.Append(k.Tenant); + } + + if (!string.IsNullOrEmpty(k.User)) + { + sb.Append(" user="); + sb.Append(k.User); + } + + return sb.ToString(); } } diff --git a/hanatui/src/Tui/MainMenuScreen.cs b/hanatui/src/Tui/MainMenuScreen.cs index 1afd7d4..00bbab4 100644 --- a/hanatui/src/Tui/MainMenuScreen.cs +++ b/hanatui/src/Tui/MainMenuScreen.cs @@ -32,15 +32,15 @@ public static class MainMenuScreen if (key is not null) { + var conn = key.Host + ":" + key.Port + + (string.IsNullOrEmpty(key.Tenant) ? "" : "@" + key.Tenant) + + " user=" + key.User; AnsiConsole.MarkupLine( - $" Key: [bold yellow]{key.Name}[/] " + - $"[dim]{key.Host}:{key.Port}" + - (string.IsNullOrEmpty(key.Tenant) ? "" : $"@{key.Tenant}") + - $" user={key.User}[/]"); + $" Key: [bold yellow]{Markup.Escape(key.Name)}[/] [dim]{Markup.Escape(conn)}[/]"); } else { - AnsiConsole.MarkupLine($" Key: [bold yellow]{keyName}[/]"); + AnsiConsole.MarkupLine($" Key: [bold yellow]{Markup.Escape(keyName)}[/]"); } AnsiConsole.WriteLine(); diff --git a/hanatui/src/Tui/OperationForms.cs b/hanatui/src/Tui/OperationForms.cs index 6e5bb71..b13e8e4 100644 --- a/hanatui/src/Tui/OperationForms.cs +++ b/hanatui/src/Tui/OperationForms.cs @@ -38,22 +38,30 @@ public static class OperationForms if (error is not null) AnsiConsole.MarkupLine($"[yellow][[WARN]] Could not fetch schemas: {Markup.Escape(error)}[/]"); - var choices = new List(schemas); - choices.Add("[dim][ Enter manually ][/]"); - choices.Add("[dim][ Cancel ][/]"); + // Use sentinel strings as values so no markup leaks into choice labels + const string manualSentinel = "__MANUAL__"; + const string cancelSentinel = "__CANCEL__"; + + var choiceValues = new List(schemas) { manualSentinel, cancelSentinel }; var prompt = new SelectionPrompt() - .Title($"[bold]{title}[/]") + .Title($"[bold]{Markup.Escape(title)}[/]") .PageSize(15) .HighlightStyle(Style.Parse("bold dodgerblue1")) - .AddChoices(choices); + .UseConverter(v => v switch + { + manualSentinel => "[ Enter manually ]", + cancelSentinel => "[ Cancel ]", + _ => v, + }) + .AddChoices(choiceValues); var selected = AnsiConsole.Prompt(prompt); - if (selected.Contains("Cancel")) + if (selected == cancelSentinel) return null; - if (selected.Contains("manually")) + if (selected == manualSentinel) { var manual = AnsiConsole.Ask("Enter schema name:").Trim(); return string.IsNullOrWhiteSpace(manual) ? null : manual;