feat: search command in REPL with topic-aware context
This commit is contained in:
parent
58410f8d9f
commit
8576cc83a2
2 changed files with 62 additions and 0 deletions
61
NozCli/Commands/SearchCommand.cs
Normal file
61
NozCli/Commands/SearchCommand.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
using NozCli.Repl;
|
||||||
|
using Spectre.Console;
|
||||||
|
|
||||||
|
namespace NozCli.Commands;
|
||||||
|
|
||||||
|
public class SearchCommand : ICommand
|
||||||
|
{
|
||||||
|
public string Name => "search";
|
||||||
|
public string Description => "Full-text search via Meilisearch";
|
||||||
|
public string Usage => "search <query> [--topic <topic>] [--limit <n>]";
|
||||||
|
|
||||||
|
private static readonly Dictionary<string, string> StatusDot = new()
|
||||||
|
{
|
||||||
|
["seed"] = "[yellow]◦[/]",
|
||||||
|
["budding"] = "[magenta]◈[/]",
|
||||||
|
["evergreen"] = "[cyan]◉[/]",
|
||||||
|
};
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(string[] args, ReplContext ctx, CancellationToken ct = default)
|
||||||
|
{
|
||||||
|
var topic = ParseFlag(args, "--topic") ?? ctx.CurrentTopic;
|
||||||
|
var limitRaw = ParseFlag(args, "--limit");
|
||||||
|
var limit = limitRaw is not null && int.TryParse(limitRaw, out var l) ? l : 10;
|
||||||
|
var query = string.Join(' ', args.Where(a => !a.StartsWith('-') &&
|
||||||
|
a != ParseFlag(args, "--topic") &&
|
||||||
|
a != ParseFlag(args, "--limit")));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine("[red]Usage:[/] search <query> [--topic <topic>] [--limit <n>]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<NozCli.Core.Models.SearchHit> hits = [];
|
||||||
|
await AnsiConsole.Status().StartAsync($"Suche nach »{query}«…", async _ =>
|
||||||
|
hits = await ctx.Api.SearchAsync(query, topic, limit, ct));
|
||||||
|
|
||||||
|
if (hits.Count == 0)
|
||||||
|
{
|
||||||
|
AnsiConsole.MarkupLine($"[dim]Keine Treffer für »{Markup.Escape(query)}«.[/]");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnsiConsole.MarkupLine($"[dim]{hits.Count} Treffer[/]\n");
|
||||||
|
|
||||||
|
foreach (var h in hits)
|
||||||
|
{
|
||||||
|
var dot = StatusDot.GetValueOrDefault(h.Status, "·");
|
||||||
|
var desc = h.Description is { Length: > 0 } d ? $"\n [dim]{Markup.Escape(d)}[/]" : "";
|
||||||
|
AnsiConsole.MarkupLine($" {dot} [cyan]{Markup.Escape(h.Slug)}[/] [white]{Markup.Escape(h.Title)}[/]{desc}");
|
||||||
|
}
|
||||||
|
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ParseFlag(string[] args, string flag)
|
||||||
|
{
|
||||||
|
var idx = Array.IndexOf(args, flag);
|
||||||
|
return idx >= 0 && idx + 1 < args.Length ? args[idx + 1] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,6 +25,7 @@ public class CommandRegistry
|
||||||
new TreeCommand(),
|
new TreeCommand(),
|
||||||
new MkdirCommand(),
|
new MkdirCommand(),
|
||||||
new MvCommand(),
|
new MvCommand(),
|
||||||
|
new SearchCommand(),
|
||||||
new LinkCommand(),
|
new LinkCommand(),
|
||||||
new TemplatesCommand(),
|
new TemplatesCommand(),
|
||||||
new VersionCommand(),
|
new VersionCommand(),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue