diff --git a/PwshSpectreConsole/private/classes/PwshSpectreConsole.Recording.cs b/PwshSpectreConsole/private/classes/PwshSpectreConsole.Recording.cs index fd7e8b95..446344dc 100644 --- a/PwshSpectreConsole/private/classes/PwshSpectreConsole.Recording.cs +++ b/PwshSpectreConsole/private/classes/PwshSpectreConsole.Recording.cs @@ -44,10 +44,95 @@ public string GetOutput() } } + internal class AsciiCastInput : IAnsiConsoleInput + { + private readonly Queue<(ConsoleKeyInfo?, int)> _input; + private readonly Random _random = new Random(); + + public AsciiCastInput() + { + _input = new Queue<(ConsoleKeyInfo?, int)>(); + } + + public void PushText(string input, int keypressDelayMs) + { + if (input is null) + { + throw new ArgumentNullException(nameof(input)); + } + + foreach (var character in input) + { + PushCharacter(character, keypressDelayMs); + } + } + + public void PushTextWithEnter(string input, int keypressDelayMs) + { + PushText(input, keypressDelayMs); + PushKey(ConsoleKey.Enter, keypressDelayMs); + } + + public void PushCharacter(char input, int keypressDelayMs) + { + var delay = keypressDelayMs + _random.Next((int)(keypressDelayMs * -.2), (int)(keypressDelayMs * .2)); + + switch (input) + { + case '↑': + PushKey(ConsoleKey.UpArrow, keypressDelayMs); + break; + case '↓': + PushKey(ConsoleKey.DownArrow, keypressDelayMs); + break; + case '↲': + PushKey(ConsoleKey.Enter, keypressDelayMs); + break; + case '¦': + _input.Enqueue((null, delay)); + break; + default: + var control = char.IsUpper(input); + _input.Enqueue((new ConsoleKeyInfo(input, (ConsoleKey)input, false, false, control), delay)); + break; + } + } + + public void PushKey(ConsoleKey input, int keypressDelayMs) + { + var delay = keypressDelayMs + _random.Next((int)(keypressDelayMs * -.2), (int)(keypressDelayMs * .2)); + _input.Enqueue((new ConsoleKeyInfo((char)input, input, false, false, false), delay)); + } + + public bool IsKeyAvailable() + { + return _input.Count > 0; + } + + public ConsoleKeyInfo? ReadKey(bool intercept) + { + if (_input.Count == 0) + { + throw new InvalidOperationException("No input available."); + } + + var result = _input.Dequeue(); + + Thread.Sleep(result.Item2); + return result.Item1; + } + + public Task ReadKeyAsync(bool intercept, CancellationToken cancellationToken) + { + return Task.FromResult(ReadKey(intercept)); + } + } + public class RecordingConsole : IAnsiConsole { private IAnsiConsole _ansiConsole; private AsciiCastWriter _writer; + private AsciiCastInput _input; public RecordingConsole(int width, int height) { @@ -56,6 +141,7 @@ public RecordingConsole(int width, int height) var asciiCast = new AsciiCastWriter(); var output = new AnsiConsoleOutput(asciiCast); + var input = new AsciiCastInput(); var settings = new AnsiConsoleSettings { @@ -64,6 +150,7 @@ public RecordingConsole(int width, int height) Interactive = InteractionSupport.Yes, Enrichment = profileEnrichment, Out = output + Input = input }; var console = AnsiConsole.Create(settings); @@ -96,7 +183,7 @@ public string GetAsciiCastRecording(string title) public Profile Profile => _ansiConsole.Profile; public IAnsiConsoleCursor Cursor => _ansiConsole.Cursor; - public IAnsiConsoleInput Input => _ansiConsole.Input; + public AsciiCastInput Input => _input; public IExclusivityMode ExclusivityMode => _ansiConsole.ExclusivityMode; public RenderPipeline Pipeline => _ansiConsole.Pipeline;