Public Access
Improve settings logs agent and renderer
PR and Push Build/Test / build-and-test (push) Successful in 8m13s
PR and Push Build/Test / build-and-test (push) Successful in 8m13s
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
namespace MeetingAssistant.Workflow;
|
||||
|
||||
internal sealed class WorkflowRulesEditorMarkdownLinkResolver
|
||||
{
|
||||
private static readonly string[] ImageUriSchemes = ["http", "https", "file"];
|
||||
private readonly IReadOnlyList<string> rootPaths;
|
||||
private readonly IReadOnlyList<string> recursiveRootPaths;
|
||||
private readonly Dictionary<string, string> resolvedFiles = new(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public WorkflowRulesEditorMarkdownLinkResolver(
|
||||
IEnumerable<string> rootPaths,
|
||||
IEnumerable<string>? recursiveRootPaths = null)
|
||||
{
|
||||
this.rootPaths = rootPaths
|
||||
.Where(path => !string.IsNullOrWhiteSpace(path))
|
||||
.Select(path => Path.GetFullPath(path))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
this.recursiveRootPaths = (recursiveRootPaths ?? [])
|
||||
.Where(path => !string.IsNullOrWhiteSpace(path))
|
||||
.Select(path => Path.GetFullPath(path))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static WorkflowRulesEditorMarkdownLinkResolver Empty { get; } = new([]);
|
||||
|
||||
public static WorkflowRulesEditorMarkdownLinkResolver FromOptions(MeetingAssistantOptions options)
|
||||
{
|
||||
var artifactRoots = new[]
|
||||
{
|
||||
VaultPath.Resolve(options.Vault, options.Vault.AssistantContextFolder),
|
||||
VaultPath.Resolve(options.Vault, options.Vault.MeetingNotesFolder),
|
||||
VaultPath.Resolve(options.Vault, options.Vault.SummariesFolder),
|
||||
VaultPath.Resolve(options.Vault, options.Vault.TranscriptsFolder),
|
||||
VaultPath.Resolve(options.Vault, options.Vault.ProjectsFolder)
|
||||
};
|
||||
|
||||
return new WorkflowRulesEditorMarkdownLinkResolver([
|
||||
..artifactRoots,
|
||||
Environment.CurrentDirectory,
|
||||
AppContext.BaseDirectory
|
||||
], artifactRoots);
|
||||
}
|
||||
|
||||
public bool TryResolveImageUri(string? target, out Uri uri)
|
||||
{
|
||||
uri = null!;
|
||||
if (string.IsNullOrWhiteSpace(target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(target, UriKind.Absolute, out uri!) &&
|
||||
ImageUriSchemes.Contains(uri.Scheme, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryResolveExistingFilePath(target, out var path))
|
||||
{
|
||||
uri = new Uri(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public string ResolveOpenTarget(string? target)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(target))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(target, UriKind.Absolute, out var uri))
|
||||
{
|
||||
return uri.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase)
|
||||
? uri.LocalPath
|
||||
: uri.ToString();
|
||||
}
|
||||
|
||||
return TryResolveExistingFilePath(target, out var path)
|
||||
? path
|
||||
: target;
|
||||
}
|
||||
|
||||
private bool TryResolveExistingFilePath(string target, out string path)
|
||||
{
|
||||
path = "";
|
||||
if (Path.IsPathRooted(target) && File.Exists(target))
|
||||
{
|
||||
path = Path.GetFullPath(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (resolvedFiles.TryGetValue(target, out var cachedPath))
|
||||
{
|
||||
path = cachedPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TryResolveDirectRelativePath(target, out path) ||
|
||||
TryFindRelativePathInRoots(target, out path))
|
||||
{
|
||||
resolvedFiles[target] = path;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryResolveDirectRelativePath(string target, out string path)
|
||||
{
|
||||
foreach (var root in rootPaths)
|
||||
{
|
||||
try
|
||||
{
|
||||
var candidate = Path.GetFullPath(target, root);
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
path = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryFindRelativePathInRoots(string target, out string path)
|
||||
{
|
||||
path = "";
|
||||
var normalizedSuffix = NormalizeRelativePath(target);
|
||||
if (string.IsNullOrWhiteSpace(normalizedSuffix))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var fileName = Path.GetFileName(normalizedSuffix);
|
||||
if (string.IsNullOrWhiteSpace(fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var root in recursiveRootPaths.Where(Directory.Exists))
|
||||
{
|
||||
try
|
||||
{
|
||||
var match = Directory
|
||||
.EnumerateFiles(root, fileName, SearchOption.AllDirectories)
|
||||
.Where(candidate => NormalizeRelativePath(Path.GetRelativePath(root, candidate))
|
||||
.EndsWith(normalizedSuffix, StringComparison.OrdinalIgnoreCase))
|
||||
.OrderByDescending(File.GetLastWriteTimeUtc)
|
||||
.FirstOrDefault();
|
||||
if (match is not null)
|
||||
{
|
||||
path = match;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string NormalizeRelativePath(string path)
|
||||
{
|
||||
return path
|
||||
.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar)
|
||||
.TrimStart(Path.DirectorySeparatorChar);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user