feat(ui): complete Avalonia UI port with 7 main pages, tool settings & top MenuBar
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
using ClawdDotNet.Core.Storage;
|
||||
|
||||
namespace ClawdDotNet.Tools.FileRW;
|
||||
|
||||
/// <summary>
|
||||
@@ -11,6 +13,11 @@ namespace ClawdDotNet.Tools.FileRW;
|
||||
/// Ohne abschließenden Verzeichnistrenner erlaubte ein Root wie
|
||||
/// "…\Agent-X\Workspace" damit auch Zugriffe auf "…\Agent-X\Workspace-Backup\…" —
|
||||
/// ein fremdes Verzeichnis, das zufällig mit demselben Präfix beginnt.
|
||||
///
|
||||
/// Der Vergleich selbst liegt in <see cref="PathBoundary"/>: Unter Linux
|
||||
/// unterscheidet das Dateisystem Groß- und Kleinschreibung, und symbolische
|
||||
/// Verknüpfungen können aus dem Workspace hinauszeigen, ohne dass die reine
|
||||
/// Pfadrechnung das sieht.
|
||||
/// </summary>
|
||||
public static class WorkspacePath
|
||||
{
|
||||
@@ -31,6 +38,12 @@ public static class WorkspacePath
|
||||
}
|
||||
|
||||
// Alternate Data Streams (datei.txt:versteckt) umgehen die Endungsprüfung.
|
||||
//
|
||||
// Die Prüfung trägt unter Linux zusätzlich: Dort meldet Path.IsPathRooted für
|
||||
// "C:\Windows\…" false — ein Windows-Pfad ist unter Linux ein gewöhnlicher
|
||||
// Dateiname. Ohne diese Zeile käme er als relativer Pfad durch. Ein Doppelpunkt
|
||||
// im Dateinamen ist unter Linux zwar erlaubt, aber nichts, was ein Agent
|
||||
// braucht; die Einschränkung kostet uns nichts.
|
||||
if (value.Contains(':'))
|
||||
{
|
||||
throw new UnauthorizedAccessException(
|
||||
@@ -51,33 +64,13 @@ public static class WorkspacePath
|
||||
|
||||
/// <summary>
|
||||
/// Prüft, ob <paramref name="candidate"/> im Verzeichnis <paramref name="root"/> liegt.
|
||||
/// Der Vergleich erfolgt auf Verzeichnisgrenzen, nicht auf Zeichenketten-Präfixen.
|
||||
/// Der Vergleich erfolgt auf Verzeichnisgrenzen, nicht auf Zeichenketten-Präfixen,
|
||||
/// und löst symbolische Verknüpfungen auf.
|
||||
/// </summary>
|
||||
public static bool IsInside(string candidate, string root)
|
||||
{
|
||||
var normalizedRoot = NormalizeDirectory(root);
|
||||
var normalizedCandidate = Path.GetFullPath(candidate);
|
||||
|
||||
// Der Root selbst gilt als innerhalb.
|
||||
if (string.Equals(
|
||||
normalizedCandidate.TrimEnd(Path.DirectorySeparatorChar),
|
||||
normalizedRoot.TrimEnd(Path.DirectorySeparatorChar),
|
||||
StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Der Trenner ist entscheidend: Ohne ihn gälte "…\Workspace-Backup" als
|
||||
// Teil von "…\Workspace".
|
||||
return normalizedCandidate.StartsWith(normalizedRoot, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
=> PathBoundary.IsInside(candidate, root);
|
||||
|
||||
/// <summary>Absoluter Pfad mit genau einem abschließenden Trenner.</summary>
|
||||
private static string NormalizeDirectory(string path)
|
||||
{
|
||||
var full = Path.GetFullPath(path);
|
||||
return full.EndsWith(Path.DirectorySeparatorChar)
|
||||
? full
|
||||
: full + Path.DirectorySeparatorChar;
|
||||
}
|
||||
=> PathBoundary.NormalizeDirectory(path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user