diff --git a/source/Cute.Lib/Extensions/StringExtensions.cs b/source/Cute.Lib/Extensions/StringExtensions.cs index cee3397..5d0f87a 100644 --- a/source/Cute.Lib/Extensions/StringExtensions.cs +++ b/source/Cute.Lib/Extensions/StringExtensions.cs @@ -64,6 +64,61 @@ public static string Snip(this string text, int snipTo) return text[..(snipTo - 1)] + ".."; } + public static IEnumerable GetFixedLines(this ReadOnlySpan input, int maxLineLength = 80) + { + var lines = new List(); + + while (!input.IsEmpty) + { + // Find the maximum slice we can take for this line + var length = Math.Min(maxLineLength, input.Length); + var slice = input[..length]; // Using the range operator here for slicing + + // Find the first occurrence of \r or \n + var firstNewlineIndex = slice.IndexOfAny('\r', '\n'); + // Find the last occurrence of a space + var lastSpaceIndex = slice.LastIndexOf(' '); + + if (lastSpaceIndex != -1 && firstNewlineIndex > lastSpaceIndex) + { + lastSpaceIndex = -1; + } + + if (lastSpaceIndex != -1 && length < maxLineLength) + { + lastSpaceIndex = -1; + } + + // Break at the first newline character + if (firstNewlineIndex != -1 && (firstNewlineIndex < lastSpaceIndex || lastSpaceIndex == -1)) + { + lines.Add(slice[..firstNewlineIndex].ToString()); + + // Handle \r\n as a single line break + if (firstNewlineIndex + 1 < input.Length && input[firstNewlineIndex] == '\r' && input[firstNewlineIndex + 1] == '\n') + input = input[(firstNewlineIndex + 2)..]; // Skip \r\n using range + else + input = input[(firstNewlineIndex + 1)..]; // Skip \r or \n using range + + continue; + } + + // Break at the last space if no newline is found earlier + if (lastSpaceIndex != -1) + { + lines.Add(slice[..lastSpaceIndex].ToString()); + input = input[(lastSpaceIndex + 1)..]; // Skip the space using range + continue; + } + + // If no space or newline was found, just break at max length + lines.Add(slice.ToString()); + input = input[length..]; // Move to the next chunk using range operator + } + + return lines; + } + [GeneratedRegex(@"\s+")] private static partial Regex MultiWhiteSpace(); diff --git a/source/Cute/Commands/Chat/ChatCommand.cs b/source/Cute/Commands/Chat/ChatCommand.cs index 84964b0..a295a22 100644 --- a/source/Cute/Commands/Chat/ChatCommand.cs +++ b/source/Cute/Commands/Chat/ChatCommand.cs @@ -15,6 +15,7 @@ using Cute.Lib.Extensions; using Cute.Services; using Cute.Services.CliCommandInfo; +using Cute.Services.Markdown; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using OpenAI.Chat; @@ -182,12 +183,13 @@ await AnsiConsole.Status() AnsiConsole.MarkupLine(_console.FormatToMarkup($"Presence Penalty : {chatCompletionOptions.PresencePenalty}", Globals.StyleDim, Globals.StyleSubHeading)); _console.WriteBlankLine(); _console.WriteRuler(); - _console.WriteBlankLine(); } + _console.WriteBlankLine(); + if (settings.Key is not null) { - _console.WriteNormalWithHighlights($"Press {""} on a blank line to submit your prompt. (i.e. type your prompt and press {""} twice to submit your prompt).", Globals.StyleHeading); + _console.WriteNormalWithHighlights($"Press {""} on a blank line to submit your prompt. (i.e. type your prompt and press {""} twice).", Globals.StyleHeading); _console.WriteBlankLine(); } @@ -240,7 +242,25 @@ await AnsiConsole.Status() _console.WriteBlankLine(); - if (botResponse.Answer is not null) _console.WriteSubHeading(botResponse.Answer); + if (botResponse.Answer is not null) + { + if (settings.Key == null) + { + _console.WriteSubHeading(botResponse.Answer); + } + else + { + MarkdownConsole.Write(botResponse.Answer); + } + } + + if (botResponse.Type == "Exit") + { + _console.WriteBlankLine(); + _console.WriteAlert($"{SayBye()}!"); + _console.WriteBlankLine(); + break; + } if (botResponse.Question is not null && botResponse.Question.Contains("Shall we give it a shot?")) { @@ -249,8 +269,11 @@ await AnsiConsole.Status() continue; } - _console.WriteBlankLine(); - if (botResponse.Question is not null) _console.WriteSubHeading(botResponse.Question); + if (botResponse.Question is not null) + { + _console.WriteBlankLine(); + _console.WriteSubHeading(botResponse.Question); + } } return 0; @@ -380,7 +403,8 @@ When asked your name you always quote something profound from HHGTTG. "answer" is a JSON string and contains your best answer. Keep them punchy. "question" is a JSON string and contains your next question for the user to help them reach their goal. "queryOrCommand" is a JSON string and contains the accurate CLI command or GraphQl query that will achieve the goal. - "type" contains "GraphQL" or "CLI" depending on what is in "queryOrCommand". + "type" contains "GraphQL" or "CLI" to execte commands depending on what is in "queryOrCommand". + "type" contains "Exit" if the user wants to leave the conversation or quit the app. "queryOrCommand" and "type" MUST only supplied when you ask "Shall we give it a shot?" when the goal is clear to you. These fields MUST always contain valid JSON strings. No other data types are allowed in them. Only output the JSON structure, one object per response. @@ -436,13 +460,13 @@ If a fiels is a Link to another entry then the child entry fields are valid to u When generating CLI commands with parameters, the use of single quotes (') to delimit a string is NOT supported. Always use doube quotes (") to delimit strings for commands and escape any double quotes with a slash (\") Never escape double quotes unless they are inside unescaped quotes on the command line - Regex expressions are currently NOT supported in edit or find or replace expressions. Don't suggest them + Regex expressions are NOT supported in edit or find or replace expressions. Don't suggest them ever. """"; } private static string BuildContentTypesPromptInfo(IEnumerable contentTypes) { - string[] excludePrefix = ["ux", "ui", "cute", "meta"]; + string[] excludePrefix = ["ux", "ui", "meta"]; var sbContentTypesInfo = new StringBuilder(); foreach (var contentType in contentTypes.OrderBy(ct => ct.Id())) diff --git a/source/Cute/Commands/_Legacy/WebCommand.cs b/source/Cute/Commands/_Legacy/WebCommand.cs index 963bf47..c75df2d 100644 --- a/source/Cute/Commands/_Legacy/WebCommand.cs +++ b/source/Cute/Commands/_Legacy/WebCommand.cs @@ -217,7 +217,7 @@ private async Task DisplaySettings(HttpContext context) private const string _prettifyColors = """