NoteLinkHelper inserts wikilinks into an existing 'Siehe auch' section
or appends one. ApplyPlaceholders fills {{title}}/{{date}}/{{topic}} in
template bodies. new --template <name> fetches a server-side template
before opening the editor. SlugGuard moved to NozCli.Core in a previous
session — remove stale copy from NozCli.
120 lines
4 KiB
C#
120 lines
4 KiB
C#
using System.Diagnostics;
|
|
using NozCli.Core.Client;
|
|
using NozCli.Core.Models;
|
|
using NozCli.Repl;
|
|
using Spectre.Console;
|
|
|
|
namespace NozCli.Commands;
|
|
|
|
public class NewCommand : ICommand
|
|
{
|
|
public string Name => "new";
|
|
public string Description => "Create a new note and open it in nano (or -c for VS Code)";
|
|
public string Usage => "new [-c] [--template <name>] <slug-or-name>";
|
|
|
|
public async Task ExecuteAsync(string[] args, ReplContext ctx, CancellationToken ct = default)
|
|
{
|
|
var useCode = args.Contains("-c") || args.Contains("--code");
|
|
var templateName = ParseFlag(args, "--template") ?? ParseFlag(args, "-t");
|
|
var nameArg = args.FirstOrDefault(a => !a.StartsWith('-'));
|
|
if (nameArg is null) { AnsiConsole.MarkupLine("[red]Usage: new [-c] [--template <name>] <slug-or-name>[/]"); return; }
|
|
|
|
var slug = ctx.ResolveNewSlug(nameArg);
|
|
|
|
if (ctx.NoteCache.Any(n => n.Slug == slug))
|
|
{
|
|
AnsiConsole.MarkupLine($"[yellow]Notiz existiert bereits:[/] {slug} [dim](use 'edit' to modify it)[/]");
|
|
return;
|
|
}
|
|
|
|
var topic = ctx.CurrentTopic ?? (slug.Contains('/') ? slug.Split('/')[0] : slug);
|
|
var title = ToTitle(slug.Split('/').Last());
|
|
var lang = "de";
|
|
|
|
string noteBody;
|
|
|
|
if (templateName is not null)
|
|
{
|
|
TemplateContent? tmpl = null;
|
|
await AnsiConsole.Status().StartAsync($"Lade Template '{templateName}'…", async _ =>
|
|
tmpl = await ctx.Api.GetTemplateAsync(templateName, ct));
|
|
|
|
if (tmpl is null)
|
|
{
|
|
AnsiConsole.MarkupLine($"[red]Template '{templateName}' nicht gefunden.[/] Verfügbare Templates mit [white]templates[/] anzeigen.");
|
|
return;
|
|
}
|
|
|
|
noteBody = NoteLinkHelper.ApplyPlaceholders(tmpl.Body, title, topic, lang, "seed");
|
|
}
|
|
else
|
|
{
|
|
noteBody = $"# {title}\n\n";
|
|
}
|
|
|
|
var template = $"""
|
|
---
|
|
title: "{title}"
|
|
description: ""
|
|
topic: {topic}
|
|
status: seed
|
|
lang: {lang}
|
|
---
|
|
|
|
{noteBody.TrimStart()}
|
|
""";
|
|
|
|
var tmp = Path.Combine(Path.GetTempPath(), $"noz-new-{slug.Replace('/', '-')}.md");
|
|
await File.WriteAllTextAsync(tmp, template);
|
|
|
|
string markdown;
|
|
try
|
|
{
|
|
var (editor, editorArgs) = useCode
|
|
? EditCommand.ResolveCode(tmp, ctx.Config.CodeEditor)
|
|
: EditCommand.ResolveTerminal(tmp, ctx.Config.Editor);
|
|
|
|
AnsiConsole.MarkupLine($"[dim]Opening {editor} …[/]");
|
|
var proc = Process.Start(new ProcessStartInfo
|
|
{
|
|
FileName = editor,
|
|
Arguments = editorArgs,
|
|
UseShellExecute = false,
|
|
});
|
|
proc?.WaitForExit();
|
|
|
|
markdown = await File.ReadAllTextAsync(tmp);
|
|
}
|
|
finally
|
|
{
|
|
if (File.Exists(tmp)) File.Delete(tmp);
|
|
}
|
|
|
|
if (markdown.Trim() == template.Trim())
|
|
{
|
|
AnsiConsole.MarkupLine("[dim]No changes — note not created.[/]");
|
|
return;
|
|
}
|
|
|
|
var push = AnsiConsole.Confirm($"Push [cyan]{slug}[/] to server?");
|
|
if (!push) { AnsiConsole.MarkupLine("[dim]Discarded.[/]"); return; }
|
|
|
|
await AnsiConsole.Status().StartAsync("Creating…", async _ =>
|
|
{
|
|
await ctx.Api.PutNoteAsync(slug, markdown, ct);
|
|
ctx.NoteCache = await ctx.Api.GetNotesAsync(ct);
|
|
});
|
|
|
|
AnsiConsole.MarkupLine($"[green]✓[/] {slug} created");
|
|
}
|
|
|
|
private static string ToTitle(string slug) =>
|
|
string.Join(' ', slug.Split('-').Select(w =>
|
|
w.Length > 0 ? char.ToUpper(w[0]) + w[1..] : w));
|
|
|
|
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;
|
|
}
|
|
}
|