feat(themes): custom theme loading with file-stamp cache
This commit is contained in:
@@ -7,9 +7,11 @@ public sealed class ThemeRegistry
|
|||||||
public const string DefaultSlug = HellionArctic.Slug;
|
public const string DefaultSlug = HellionArctic.Slug;
|
||||||
|
|
||||||
private readonly Dictionary<string, Theme> _builtIns;
|
private readonly Dictionary<string, Theme> _builtIns;
|
||||||
|
private readonly Dictionary<string, (Theme Theme, DateTime Stamp)> _customCache = new(StringComparer.OrdinalIgnoreCase);
|
||||||
|
private readonly string? _customThemesDir;
|
||||||
private Theme _active;
|
private Theme _active;
|
||||||
|
|
||||||
public ThemeRegistry()
|
public ThemeRegistry(string? customThemesDir = null)
|
||||||
{
|
{
|
||||||
_builtIns = new Dictionary<string, Theme>(StringComparer.OrdinalIgnoreCase)
|
_builtIns = new Dictionary<string, Theme>(StringComparer.OrdinalIgnoreCase)
|
||||||
{
|
{
|
||||||
@@ -20,23 +22,75 @@ public sealed class ThemeRegistry
|
|||||||
{ MintGrove.Slug, MintGrove.Build() },
|
{ MintGrove.Slug, MintGrove.Build() },
|
||||||
};
|
};
|
||||||
_active = _builtIns[DefaultSlug];
|
_active = _builtIns[DefaultSlug];
|
||||||
|
_customThemesDir = customThemesDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Theme Active => _active;
|
public Theme Active => _active;
|
||||||
|
|
||||||
// Active-Lookup: liefert das angeforderte Theme oder fällt auf Hellion Arctic
|
|
||||||
// zurück, wenn der Slug unbekannt ist.
|
|
||||||
public Theme Get(string slug)
|
public Theme Get(string slug)
|
||||||
{
|
{
|
||||||
return _builtIns.TryGetValue(slug, out var theme)
|
if (_builtIns.TryGetValue(slug, out var b)) return b;
|
||||||
? theme
|
|
||||||
: _builtIns[DefaultSlug];
|
var custom = LoadCustomBySlug(slug);
|
||||||
|
if (custom != null) return custom;
|
||||||
|
|
||||||
|
return _builtIns[DefaultSlug];
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Theme> AllBuiltIns() => _builtIns.Values;
|
public IEnumerable<Theme> AllBuiltIns() => _builtIns.Values;
|
||||||
|
|
||||||
public void Switch(string slug)
|
public IEnumerable<Theme> AllCustom() => RefreshCustomCache();
|
||||||
|
|
||||||
|
public void Switch(string slug) => _active = Get(slug);
|
||||||
|
|
||||||
|
// Custom-Themes werden lazy aus dem Verzeichnis geladen, Cache mit
|
||||||
|
// LastWriteTime-Token. Eine geänderte JSON wird beim nächsten Lookup
|
||||||
|
// neu eingelesen.
|
||||||
|
private Theme? LoadCustomBySlug(string slug)
|
||||||
{
|
{
|
||||||
_active = Get(slug);
|
if (_customThemesDir is null) return null;
|
||||||
|
if (!Directory.Exists(_customThemesDir)) return null;
|
||||||
|
|
||||||
|
foreach (var theme in RefreshCustomCache())
|
||||||
|
if (string.Equals(theme.Slug, slug, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return theme;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Theme> RefreshCustomCache()
|
||||||
|
{
|
||||||
|
if (_customThemesDir is null || !Directory.Exists(_customThemesDir))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
var seenSlugs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
foreach (var path in Directory.EnumerateFiles(_customThemesDir, "*.json"))
|
||||||
|
{
|
||||||
|
Theme? theme = null;
|
||||||
|
var stamp = File.GetLastWriteTimeUtc(path);
|
||||||
|
var key = path;
|
||||||
|
if (_customCache.TryGetValue(key, out var cached) && cached.Stamp == stamp)
|
||||||
|
{
|
||||||
|
theme = cached.Theme;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
theme = ThemeJsonLoader.LoadFromFile(path);
|
||||||
|
_customCache[key] = (theme, stamp);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Logging passiert in Plugin.cs durch den Aufrufer; hier still
|
||||||
|
// ignorieren, damit ein einzelnes kaputtes JSON nicht alle
|
||||||
|
// Custom-Themes blockt.
|
||||||
|
_ = ex;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theme is not null && seenSlugs.Add(theme.Slug))
|
||||||
|
yield return theme;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user