diff --git a/TextLore/Commands/ClearCommand.cs b/TextLore/Commands/ClearCommand.cs new file mode 100644 index 0000000..171629e --- /dev/null +++ b/TextLore/Commands/ClearCommand.cs @@ -0,0 +1,19 @@ +using TextLore.Models; + +namespace TextLore.Commands; + +public class ClearCommand : Command +{ + public override string Name => "clear"; + public override string Description => "Clears the console."; + + public override string[] Aliases => ["cls"]; + + public override bool NoHistoryIfNoOutput => true; + + public override Task Execute(ConsoleWriter consoleOutput, string args) + { + consoleOutput.Clear(); + return Task.FromResult(CommandResult.Success("Console cleared!")); + } +} \ No newline at end of file diff --git a/TextLore/Commands/HelpCommand.cs b/TextLore/Commands/HelpCommand.cs new file mode 100644 index 0000000..f16d17e --- /dev/null +++ b/TextLore/Commands/HelpCommand.cs @@ -0,0 +1,51 @@ +using TextLore.Models; + +namespace TextLore.Commands; + +public class HelpCommand(IEnumerable commands) : Command +{ + public override string Name => "help"; + public override string Description => "Displays a list of commands and their descriptions."; + public override string Usage => "help "; + public override string[] Aliases => new[] { "h", "?" }; + + public override Task Execute(ConsoleWriter consoleOutput, string args) + { + if (string.IsNullOrWhiteSpace(args)) + { + consoleOutput.WriteLine("Available commands:"); + foreach (Command command in commands) + { + consoleOutput.WriteLine($" {command.Name} - {command.Description}"); + } + return Task.FromResult(CommandResult.Success()); + } + else + { + Command? command = commands.FirstOrDefault(c => c.Name.Equals(args, StringComparison.OrdinalIgnoreCase) || c.Aliases.Contains(args, StringComparer.OrdinalIgnoreCase)); + if (command is null) + { + consoleOutput.WriteLine($"Command \"{args[0]}\" not found."); + return Task.FromResult(CommandResult.Failure()); + } + else + { + consoleOutput.WriteLine($"Command: {command.Name}"); + + if (command.Aliases.Length > 0) + { + consoleOutput.WriteLine($"Aliases: {string.Join(", ", command.Aliases)}"); + } + + consoleOutput.WriteLine($"Description: {command.Description}"); + + if (!string.IsNullOrWhiteSpace(command.Usage)) + { + consoleOutput.WriteLine($"Usage: {command.Usage}"); + } + + return Task.FromResult(CommandResult.Success()); + } + } + } +} \ No newline at end of file diff --git a/TextLore/Commands/TestCommand.cs b/TextLore/Commands/TestCommand.cs index 8fe64c9..fabbfef 100644 --- a/TextLore/Commands/TestCommand.cs +++ b/TextLore/Commands/TestCommand.cs @@ -7,9 +7,11 @@ public class TestCommand : Command public override string Name => "test"; public override string Description => "A test command."; - public override Task Execute(ConsoleOutput consoleOutput, string[] args) + public override string[] Aliases => ["tst"]; + + public override Task Execute(ConsoleWriter consoleOutput, string args) { consoleOutput.WriteLine("This is a test command."); - return Task.FromResult(true); + return Task.FromResult(CommandResult.Success("test")); } } \ No newline at end of file diff --git a/TextLore/Components/Pages/Home.razor b/TextLore/Components/Pages/Home.razor index 8907ade..fb74bde 100644 --- a/TextLore/Components/Pages/Home.razor +++ b/TextLore/Components/Pages/Home.razor @@ -1,12 +1,19 @@ @page "/" -@using TextLore.Commands -@using TextLore.Models -Home - +TextLore + + @code { - Command[] commands = [new TestCommand()]; + List commands = [new TestCommand(), new ClearCommand()]; + + HelpCommand? helpCommand; + + protected override void OnInitialized() + { + helpCommand = new HelpCommand(commands); + commands.Add(helpCommand); + } } \ No newline at end of file diff --git a/TextLore/Components/Shared/Console.razor b/TextLore/Components/Shared/Console.razor index e48f26b..47143a2 100644 --- a/TextLore/Components/Shared/Console.razor +++ b/TextLore/Components/Shared/Console.razor @@ -18,7 +18,7 @@
- Command > + Command >
@@ -26,8 +26,16 @@
-        @((MarkupString)running)
-        @((MarkupString)output)
+        @currentOutput?.Text
+        
+            @foreach (ConsoleOutput output in consoleOutputs.Reverse())
+            {
+                

+ @output.Time.ToString("HH:mm") > @output.Command +@output.Text +

+ } +
@@ -43,20 +51,16 @@ [Parameter] public bool ShowDate { get; set; } = true; - - public string Placeholder => $"Enter a command{(HelpCommand is null ? "." : ", type 'help' for avaliable commands.")}"; - - public ConsoleInput ConsoleInput { get; set; } = new(); - public ConsoleOutput ConsoleOutput { get; set; } = new(); - [Parameter, EditorRequired] public IEnumerable Commands { get; set; } = []; [Parameter] public Command? HelpCommand { get; set; } - private string output { get; set; } = ""; - private string running { get; set; } = ""; + private string Placeholder => $"Enter a command{(HelpCommand is null ? "." : ", type 'help' for avaliable commands.")}"; + private ConsoleInput ConsoleInput { get; set; } = new(); + private List consoleOutputs = new(); + private ConsoleOutput? currentOutput; private bool disabled { get; set; } = false; private InputText? inputText; @@ -68,45 +72,74 @@ } } - protected override void OnInitialized() - { - ConsoleOutput.OnOutput += WriteOutput; - } - public async Task Execute(EditContext context) { - Command? command = Commands.FirstOrDefault(c=>c.Name.Equals(ConsoleInput.Text)); - command ??= HelpCommand; + string commandName = ConsoleInput.Text.Split(' ')[0]; + string commandArgs = new string(ConsoleInput.Text.Skip(commandName.Length).ToArray()).TrimStart(' '); - if(command is null) + Command? command = Commands.FirstOrDefault(c => c.Name.Equals(commandName) || c.Aliases.Contains(commandName)); + command ??= HelpCommand?.Name.Equals(commandName) == true || HelpCommand?.Aliases.Contains(commandName) == true ? HelpCommand : null; + + if (command is null) { - running = $"[Failed] Command '{ConsoleInput.Text}' not found!"; - ConsoleInput.Text = string.Empty; + await CommandNotFound(commandName); return; } disabled = true; - running = $"

"; - running += $"{ConsoleInput.Time.ToString("HH:mm")} > {ConsoleInput.Text}{Environment.NewLine}"; + ConsoleWriter consoleWriter = new ConsoleWriter(); + ConsoleOutput consoleOutput = new ConsoleOutput(); - bool success = await command.Execute(ConsoleOutput, []); + consoleWriter.OnOutput += (_, e) => WriteOutput(consoleOutput, e); + consoleWriter.OnClear += (_, _) => consoleOutputs.Clear(); + consoleOutput.Command = ConsoleInput.Text; + currentOutput = consoleOutput; - running += "

"; + CommandResult commandResult = await command.Execute(consoleWriter, commandArgs); - output = running + output; + currentOutput = new() + { + Text = $"{(commandResult.IsSuccess ? "[Success]" : "[Failed]")} {commandResult.Message}" + }; - running = success ? $"[Success]" : $"[Failed]"; + if (!command.NoHistoryIfNoOutput || !string.IsNullOrWhiteSpace(consoleOutput.Text)) + { + consoleOutputs.Add(consoleOutput); + } ConsoleInput.Text = string.Empty; disabled = false; StateHasChanged(); } - public void WriteOutput(object? sender, ConsoleOutputEventArgs e) + public void WriteOutput(ConsoleOutput consoleOutput, ConsoleOutputEventArgs e) { - string newOutput = e.Message; - output = newOutput + output; + consoleOutput.Text += e.Message; StateHasChanged(); } + + private async Task CommandNotFound(string commandName) + { + currentOutput = new() + { + Text = $"[Failed] Command '{commandName}' not found!" + }; + + if(HelpCommand is null) + { + return; + } + + ConsoleWriter consoleWriter = new ConsoleWriter(); + ConsoleOutput consoleOutput = new ConsoleOutput(); + + consoleWriter.OnOutput += (_, e) => WriteOutput(consoleOutput, e); + consoleOutput.Command = ConsoleInput.Text; + consoleOutputs.Add(consoleOutput); + + CommandResult commandResult = await HelpCommand.Execute(consoleWriter, string.Empty); + + ConsoleInput.Text = string.Empty; + } } \ No newline at end of file diff --git a/TextLore/Components/_Imports.razor b/TextLore/Components/_Imports.razor index 1a5cf1b..e63cccd 100644 --- a/TextLore/Components/_Imports.razor +++ b/TextLore/Components/_Imports.razor @@ -8,3 +8,6 @@ @using Microsoft.JSInterop @using TextLore @using TextLore.Components +@using TextLore.Components.Shared +@using TextLore.Commands +@using TextLore.Models \ No newline at end of file diff --git a/TextLore/Models/Command.cs b/TextLore/Models/Command.cs index 60adc92..c3afe97 100644 --- a/TextLore/Models/Command.cs +++ b/TextLore/Models/Command.cs @@ -6,6 +6,7 @@ public abstract class Command public abstract string Description { get; } public virtual string Usage { get; } = string.Empty; public virtual string[] Aliases { get; } = []; + public virtual bool NoHistoryIfNoOutput { get; } = false; - public abstract Task Execute(ConsoleOutput consoleOutput, string[] args); + public abstract Task Execute(ConsoleWriter consoleOutput, string args); } \ No newline at end of file diff --git a/TextLore/Models/CommandResult.cs b/TextLore/Models/CommandResult.cs new file mode 100644 index 0000000..a314729 --- /dev/null +++ b/TextLore/Models/CommandResult.cs @@ -0,0 +1,22 @@ +namespace TextLore.Models; + +public class CommandResult(string message, bool success) +{ + public string Message { get; } = message; + public bool IsSuccess { get; } = success; + + public static CommandResult Success(string message = "") + { + return new(message, true); + } + + public static CommandResult Failure(string message = "") + { + return new(message, false); + } + + public static implicit operator CommandResult(string message) + { + return Success(message); + } +} \ No newline at end of file diff --git a/TextLore/Models/ConsoleOutput.cs b/TextLore/Models/ConsoleOutput.cs index 4b74ae8..f245bec 100644 --- a/TextLore/Models/ConsoleOutput.cs +++ b/TextLore/Models/ConsoleOutput.cs @@ -2,45 +2,7 @@ public class ConsoleOutput { - public event EventHandler? OnOutput; - - public void Write(string message, ConsoleMessageType consoleMessageType = ConsoleMessageType.Default) - { - OnOutput?.Invoke(this, new(message, consoleMessageType)); - } - - public void WriteLine(string message) - { - Write(message + Environment.NewLine); - } - - public void WriteError(string message) - { - Write($"[ERROR] {message}", ConsoleMessageType.Error); - } - - public void WriteErrorLine(string message) - { - WriteError(message + Environment.NewLine); - } - - public void WriteWarning(string message) - { - Write($"[WARNING] {message}", ConsoleMessageType.Warning); - } - - public void WriteWarningLine(string message) - { - WriteWarning(message + Environment.NewLine); - } - - public void WriteInfo(string message) - { - Write($"[INFO] {message}", ConsoleMessageType.Info); - } - - public void WriteInfoLine(string message) - { - WriteInfo(message + Environment.NewLine); - } + public string Text { get; set; } = string.Empty; + public string Command { get; set; } = string.Empty; + public DateTime Time { get; } = DateTime.UtcNow; } \ No newline at end of file diff --git a/TextLore/Models/ConsoleWriter.cs b/TextLore/Models/ConsoleWriter.cs new file mode 100644 index 0000000..9914923 --- /dev/null +++ b/TextLore/Models/ConsoleWriter.cs @@ -0,0 +1,52 @@ +namespace TextLore.Models; + +public class ConsoleWriter +{ + public event EventHandler? OnOutput; + + public event EventHandler? OnClear; + public void Write(string message, ConsoleMessageType consoleMessageType = ConsoleMessageType.Default) + { + OnOutput?.Invoke(this, new(message, consoleMessageType)); + } + + public void WriteLine(string message) + { + Write(message + Environment.NewLine); + } + + public void WriteError(string message) + { + Write($"[ERROR] {message}", ConsoleMessageType.Error); + } + + public void WriteErrorLine(string message) + { + WriteError(message + Environment.NewLine); + } + + public void WriteWarning(string message) + { + Write($"[WARNING] {message}", ConsoleMessageType.Warning); + } + + public void WriteWarningLine(string message) + { + WriteWarning(message + Environment.NewLine); + } + + public void WriteInfo(string message) + { + Write($"[INFO] {message}", ConsoleMessageType.Info); + } + + public void WriteInfoLine(string message) + { + WriteInfo(message + Environment.NewLine); + } + + public void Clear() + { + OnClear?.Invoke(this, EventArgs.Empty); + } +} \ No newline at end of file