refactor(di): migrate UI Window-Layer to ILogger<T> (DI-4 Slice C)

Six UI files shift from Plugin.LogProxy to ILogger<T> via
constructor injection.

Container singletons (each takes a typed ILogger plus, where it owns
nested allocations, an ILoggerFactory to spawn child loggers):
- Ui/ChatLogWindow (15 sites, plus an ILoggerFactory for the
  Popout new-call at Ui/ChatLogWindow.cs:2417)
- Ui/Settings (SettingsWindow): no own sites, but takes an
  ILoggerFactory so it can hand typed loggers to its three migrated
  settings tabs (General, the other six tabs stay unchanged)
- Ui/DbViewer (3 sites)

Nested instances allocated by parent containers:
- Ui/Popout (7 sites, ILogger<Popout> as the new 4th ctor arg passed
  from ChatLogWindow)
- Ui/SettingsTabs/ThemeAndLayout (1 site)
- Ui/SettingsTabs/FontsAndColours (1 site)
- Ui/SettingsTabs/DataManagement (15 sites)

PluginHostFactory factory lambdas updated for ChatLogWindow,
SettingsWindow and DbViewer to resolve the new logger args.
This commit is contained in:
2026-05-17 10:26:47 +02:00
parent 7a1bd1babc
commit c955f30422
8 changed files with 93 additions and 60 deletions
+13 -3
View File
@@ -153,9 +153,19 @@ internal static class PluginHostFactory
// host never AddWindow()s them; PluginLifecycle does that on the
// framework thread once C3 wires it up (see plan §2 service order).
// -----------------------------------------------------------------
services.AddSingleton(sp => new ChatLogWindow(sp.GetRequiredService<Plugin>()));
services.AddSingleton(sp => new SettingsWindow(sp.GetRequiredService<Plugin>()));
services.AddSingleton(sp => new DbViewer(sp.GetRequiredService<Plugin>()));
services.AddSingleton(sp => new ChatLogWindow(
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<ILogger<ChatLogWindow>>(),
sp.GetRequiredService<ILoggerFactory>()
));
services.AddSingleton(sp => new SettingsWindow(
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<ILoggerFactory>()
));
services.AddSingleton(sp => new DbViewer(
sp.GetRequiredService<Plugin>(),
sp.GetRequiredService<ILogger<DbViewer>>()
));
services.AddSingleton(sp => new InputPreview(sp.GetRequiredService<ChatLogWindow>()));
services.AddSingleton(sp => new CommandHelpWindow(sp.GetRequiredService<ChatLogWindow>()));
services.AddSingleton(sp => new SeStringDebugger(sp.GetRequiredService<Plugin>()));
+27 -17
View File
@@ -22,6 +22,7 @@ using HellionChat.Resources;
using HellionChat.Util;
using Lumina.Excel.Sheets;
using Lumina.Extensions;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui;
@@ -98,10 +99,19 @@ public sealed class ChatLogWindow : Window
private long FrameTime; // set every frame
internal long LastActivityTime = Environment.TickCount64;
internal ChatLogWindow(Plugin plugin)
private readonly ILogger<ChatLogWindow> _logger;
private readonly ILoggerFactory _loggerFactory;
internal ChatLogWindow(
Plugin plugin,
ILogger<ChatLogWindow> logger,
ILoggerFactory loggerFactory
)
: base($"{Plugin.PluginName}###chat2")
{
Plugin = plugin;
_logger = logger;
_loggerFactory = loggerFactory;
Salt = new Random().Next().ToString();
Size = new Vector2(500, 250);
@@ -297,7 +307,7 @@ public sealed class ChatLogWindow : Window
|| !GameFunctions.Chat.IsChannelOrExistingLinkshell(targetChannel.Value)
)
{
Plugin.LogProxy.Warning(
_logger.LogWarning(
$"Channel was set to an invalid value '{targetChannel}', ignoring"
);
return;
@@ -351,11 +361,11 @@ public sealed class ChatLogWindow : Window
{
case "hide":
CurrentHideState = HideState.User;
Plugin.LogProxy.Verbose("HideState: → User (chat hide command)");
_logger.LogTrace("HideState: → User (chat hide command)");
break;
case "show":
CurrentHideState = HideState.None;
Plugin.LogProxy.Verbose("HideState: → None (chat show command)");
_logger.LogTrace("HideState: → None (chat show command)");
break;
case "toggle":
CurrentHideState = CurrentHideState switch
@@ -365,7 +375,7 @@ public sealed class ChatLogWindow : Window
HideState.None => HideState.User,
_ => CurrentHideState,
};
Plugin.LogProxy.Verbose($"HideState: → {CurrentHideState} (chat toggle command)");
_logger.LogTrace($"HideState: → {CurrentHideState} (chat toggle command)");
break;
}
}
@@ -475,7 +485,7 @@ public sealed class ChatLogWindow : Window
else if (newTab.CurrentChannel.Channel is InputChannel.Invalid)
{
newTab.CurrentChannel = previousTab.CurrentChannel.Clone();
Plugin.LogProxy.Debug(
_logger.LogDebug(
$"[Tab] '{newTab.Name}' seeded channel from '{previousTab.Name}' "
+ $"(Channel={newTab.CurrentChannel.Channel}, TellTarget={newTab.CurrentChannel.TellTarget?.ToTargetString() ?? "null"})"
);
@@ -503,14 +513,14 @@ public sealed class ChatLogWindow : Window
if (Plugin.Config.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
{
CurrentHideState = HideState.Battle;
Plugin.LogProxy.Verbose("HideState: None → Battle");
_logger.LogTrace("HideState: None → Battle");
}
// If the chat is hidden because of battle, we reset it here
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
{
CurrentHideState = HideState.None;
Plugin.LogProxy.Verbose("HideState: Battle → None");
_logger.LogTrace("HideState: Battle → None");
}
// if the chat has no hide state and in a cutscene, set the hide state to cutscene
@@ -523,7 +533,7 @@ public sealed class ChatLogWindow : Window
if (Plugin.Functions.Chat.CheckHideFlags())
{
CurrentHideState = HideState.Cutscene;
Plugin.LogProxy.Verbose("HideState: None → Cutscene");
_logger.LogTrace("HideState: None → Cutscene");
}
}
@@ -534,7 +544,7 @@ public sealed class ChatLogWindow : Window
&& !Plugin.GposeActive
)
{
Plugin.LogProxy.Verbose($"HideState: {CurrentHideState} → None (cutscene/gpose ended)");
_logger.LogTrace($"HideState: {CurrentHideState} → None (cutscene/gpose ended)");
CurrentHideState = HideState.None;
}
@@ -542,14 +552,14 @@ public sealed class ChatLogWindow : Window
if (CurrentHideState == HideState.Cutscene && Activate)
{
CurrentHideState = HideState.CutsceneOverride;
Plugin.LogProxy.Verbose("HideState: Cutscene → CutsceneOverride (user activate)");
_logger.LogTrace("HideState: Cutscene → CutsceneOverride (user activate)");
}
// if the user hid the chat and is now activating chat, reset the hide state
if (CurrentHideState == HideState.User && Activate)
{
CurrentHideState = HideState.None;
Plugin.LogProxy.Verbose("HideState: User → None (activate)");
_logger.LogTrace("HideState: User → None (activate)");
}
if (
@@ -680,7 +690,7 @@ public sealed class ChatLogWindow : Window
}
catch (Exception ex)
{
Plugin.LogProxy.Error(ex, "Error drawing Chat Log window");
_logger.LogError(ex, "Error drawing Chat Log window");
if (!NotifiedDrawFailure)
{
Plugin.Notification.AddNotification(
@@ -1722,7 +1732,7 @@ public sealed class ChatLogWindow : Window
}
catch (Exception ex)
{
Plugin.LogProxy.Warning(ex, "Error drawing chat log");
_logger.LogWarning(ex, "Error drawing chat log");
}
}
@@ -2270,7 +2280,7 @@ public sealed class ChatLogWindow : Window
{
Plugin.Config.SeenPopOutHeaderHint = true;
Plugin.SaveConfig();
Plugin.LogProxy.Debug("v0.6.1 pop-out header hint dismissed");
_logger.LogDebug("v0.6.1 pop-out header hint dismissed");
if (openSettings)
Plugin.SettingsWindow.Toggle();
}
@@ -2408,7 +2418,7 @@ public sealed class ChatLogWindow : Window
if (PopOutWindows.Contains(tab.Identifier))
continue;
var window = new Popout(this, tab, i);
var window = new Popout(this, tab, i, _loggerFactory.CreateLogger<Popout>());
Plugin.WindowSystem.AddWindow(window);
PopOutWindows.Add(tab.Identifier);
@@ -2925,7 +2935,7 @@ public sealed class ChatLogWindow : Window
var viewport = ImGui.GetMainViewport();
var safePos = viewport.WorkPos + SafeDefaultOffset;
Position = safePos;
Plugin.LogProxy.Info(
_logger.LogInformation(
$"[Window-Recovery] {source}: snapping main window from {LastWindowPos} (size {LastWindowSize}) to {safePos}."
);
+8 -4
View File
@@ -17,6 +17,7 @@ using HellionChat.Resources;
using HellionChat.Util;
using Lumina.Data.Files;
using Lumina.Text.ReadOnly;
using Microsoft.Extensions.Logging;
using MoreLinq;
namespace HellionChat.Ui;
@@ -67,10 +68,13 @@ public class DbViewer : Window
private bool NeedsScrollReset;
public DbViewer(Plugin plugin)
private readonly ILogger<DbViewer> _logger;
public DbViewer(Plugin plugin, ILogger<DbViewer> logger)
: base("DBViewer###chat2-dbviewer")
{
Plugin = plugin;
_logger = logger;
SelectedChannels = TabsUtil.MostlyPlayer;
DateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
@@ -320,7 +324,7 @@ public class DbViewer : Window
}
catch (Exception ex)
{
Plugin.LogProxy.Error(ex, "Failed reading messages from database");
_logger.LogError(ex, "Failed reading messages from database");
}
finally
{
@@ -483,7 +487,7 @@ public class DbViewer : Window
}
catch (Exception ex)
{
Plugin.LogProxy.Error(ex, "FTS filter worker failed");
_logger.LogError(ex, "FTS filter worker failed");
}
});
}
@@ -625,7 +629,7 @@ public class DbViewer : Window
}
catch (Exception ex)
{
Plugin.LogProxy.Error(ex, "Failed creating txt backup");
_logger.LogError(ex, "Failed creating txt backup");
Notification.Content = "Error ...";
Notification.Type = NotificationType.Error;
+11 -8
View File
@@ -3,6 +3,7 @@ using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Style;
using Dalamud.Interface.Utility.Raii;
using Dalamud.Interface.Windowing;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui;
@@ -11,6 +12,7 @@ internal class Popout : Window
private readonly ChatLogWindow ChatLogWindow;
private readonly Tab Tab;
private readonly int Idx;
private readonly ILogger<Popout> _logger;
private long FrameTime;
private long LastActivityTime = Environment.TickCount64;
@@ -23,12 +25,13 @@ internal class Popout : Window
// Exposed so AutoTellTabsService can locate this window during LRU eviction.
internal Guid TabIdentifier => Tab.Identifier;
public Popout(ChatLogWindow chatLogWindow, Tab tab, int idx)
public Popout(ChatLogWindow chatLogWindow, Tab tab, int idx, ILogger<Popout> logger)
: base($"{tab.Name}##popout")
{
ChatLogWindow = chatLogWindow;
Tab = tab;
Idx = idx;
_logger = logger;
Size = new Vector2(350, 350);
SizeCondition = ImGuiCond.FirstUseEver;
@@ -175,7 +178,7 @@ internal class Popout : Window
{
Plugin.Config.SeenPopOutInputHint = true;
ChatLogWindow.Plugin.SaveConfig();
Plugin.LogProxy.Debug("Pop-Out input hint dismissed");
_logger.LogDebug("Pop-Out input hint dismissed");
if (openSettings)
ChatLogWindow.Plugin.SettingsWindow.Toggle();
}
@@ -214,13 +217,13 @@ internal class Popout : Window
if (Tab.HideInBattle && CurrentHideState == HideState.None && Plugin.InBattle)
{
CurrentHideState = HideState.Battle;
Plugin.LogProxy.Verbose($"Popout HideState [{Tab.Name}]: None -> Battle");
_logger.LogTrace($"Popout HideState [{Tab.Name}]: None -> Battle");
}
if (CurrentHideState is HideState.Battle && !Plugin.InBattle)
{
CurrentHideState = HideState.None;
Plugin.LogProxy.Verbose($"Popout HideState [{Tab.Name}]: Battle -> None");
_logger.LogTrace($"Popout HideState [{Tab.Name}]: Battle -> None");
}
if (
@@ -232,7 +235,7 @@ internal class Popout : Window
if (ChatLogWindow.Plugin.Functions.Chat.CheckHideFlags())
{
CurrentHideState = HideState.Cutscene;
Plugin.LogProxy.Verbose($"Popout HideState [{Tab.Name}]: None -> Cutscene");
_logger.LogTrace($"Popout HideState [{Tab.Name}]: None -> Cutscene");
}
}
@@ -242,7 +245,7 @@ internal class Popout : Window
&& !Plugin.GposeActive
)
{
Plugin.LogProxy.Verbose(
_logger.LogTrace(
$"Popout HideState [{Tab.Name}]: {CurrentHideState} -> None (cutscene/gpose ended)"
);
CurrentHideState = HideState.None;
@@ -251,7 +254,7 @@ internal class Popout : Window
if (CurrentHideState == HideState.Cutscene && ChatLogWindow.Activate)
{
CurrentHideState = HideState.CutsceneOverride;
Plugin.LogProxy.Verbose(
_logger.LogTrace(
$"Popout HideState [{Tab.Name}]: Cutscene -> CutsceneOverride (user activate)"
);
}
@@ -259,7 +262,7 @@ internal class Popout : Window
if (CurrentHideState == HideState.User && ChatLogWindow.Activate)
{
CurrentHideState = HideState.None;
Plugin.LogProxy.Verbose($"Popout HideState [{Tab.Name}]: User -> None (activate)");
_logger.LogTrace($"Popout HideState [{Tab.Name}]: User -> None (activate)");
}
return CurrentHideState is HideState.Cutscene or HideState.User or HideState.Battle
+5 -4
View File
@@ -6,6 +6,7 @@ using Dalamud.Utility;
using HellionChat.Resources;
using HellionChat.Ui.SettingsTabs;
using HellionChat.Util;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui;
@@ -25,7 +26,7 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
private SettingsView View = SettingsView.Overview;
private readonly SettingsOverview Overview;
internal SettingsWindow(Plugin plugin)
internal SettingsWindow(Plugin plugin, ILoggerFactory loggerFactory)
: base($"{Language.Settings_Title.Format(Plugin.PluginName)}###chat2-settings")
{
Flags = ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoScrollWithMouse;
@@ -45,13 +46,13 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
Tabs =
[
new General(Plugin, Mutable),
new ThemeAndLayout(Plugin, Mutable),
new FontsAndColours(Plugin, Mutable),
new ThemeAndLayout(Plugin, Mutable, loggerFactory.CreateLogger<ThemeAndLayout>()),
new FontsAndColours(Plugin, Mutable, loggerFactory.CreateLogger<FontsAndColours>()),
new SettingsTabs.Window(Plugin, Mutable),
new Chat(Plugin, Mutable),
new SettingsTabs.Tabs(Plugin, Mutable),
new SettingsTabs.Privacy(Plugin, Mutable),
new DataManagement(Plugin, Mutable),
new DataManagement(Plugin, Mutable, loggerFactory.CreateLogger<DataManagement>()),
new SettingsTabs.Integrations(Plugin, Mutable),
new Information(Mutable),
];
+19 -20
View File
@@ -11,6 +11,7 @@ using HellionChat.Export;
using HellionChat.Privacy;
using HellionChat.Resources;
using HellionChat.Util;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui.SettingsTabs;
@@ -18,6 +19,7 @@ internal sealed class DataManagement : ISettingsTab
{
private Plugin Plugin { get; }
private Configuration Mutable { get; }
private readonly ILogger<DataManagement> _logger;
public string Name =>
HellionStrings.Settings_Card_DataManagement_Title + "###tabs-datamanagement";
@@ -136,10 +138,11 @@ internal sealed class DataManagement : ISettingsTab
),
];
internal DataManagement(Plugin plugin, Configuration mutable)
internal DataManagement(Plugin plugin, Configuration mutable, ILogger<DataManagement> logger)
{
Plugin = plugin;
Mutable = mutable;
_logger = logger;
}
public void Draw(bool changed)
@@ -229,7 +232,7 @@ internal sealed class DataManagement : ISettingsTab
}
catch (Exception e)
{
Plugin.LogProxy.Error(e, "Unable to delete old database");
_logger.LogError(e, "Unable to delete old database");
WrapperUtil.AddNotification(
Language.Options_Database_Old_Delete_Error,
NotificationType.Error
@@ -391,9 +394,7 @@ internal sealed class DataManagement : ISettingsTab
Plugin.Config.RetentionLastRunAt = DateTimeOffset.UtcNow;
Plugin.SaveConfig();
Plugin.LogProxy.Information(
$"Manual retention run deleted {deleted} expired messages."
);
_logger.LogInformation($"Manual retention run deleted {deleted} expired messages.");
if (deleted > 0)
{
@@ -407,7 +408,7 @@ internal sealed class DataManagement : ISettingsTab
.Wait(TimeSpan.FromSeconds(5))
)
{
Plugin.LogProxy.Warning(
_logger.LogWarning(
"Retention sweep: framework refresh timed out after 5s."
);
}
@@ -420,7 +421,7 @@ internal sealed class DataManagement : ISettingsTab
}
catch (Exception e)
{
Plugin.LogProxy.Error(e, "Manual retention run failed");
_logger.LogError(e, "Manual retention run failed");
WrapperUtil.AddNotification(HellionStrings.Retention_Error, NotificationType.Error);
}
finally
@@ -568,7 +569,7 @@ internal sealed class DataManagement : ISettingsTab
}
catch (Exception e)
{
Plugin.LogProxy.Error(e, "Failed to compute cleanup preview");
_logger.LogError(e, "Failed to compute cleanup preview");
WrapperUtil.AddNotification(
HellionStrings.Cleanup_PreviewError,
NotificationType.Error
@@ -589,7 +590,7 @@ internal sealed class DataManagement : ISettingsTab
try
{
var deleted = Plugin.MessageManager.Store.CleanupRetainOnly(allowed);
Plugin.LogProxy.Information($"Privacy cleanup: deleted {deleted} messages");
_logger.LogInformation($"Privacy cleanup: deleted {deleted} messages");
if (
!Plugin
@@ -601,9 +602,7 @@ internal sealed class DataManagement : ISettingsTab
.Wait(TimeSpan.FromSeconds(5))
)
{
Plugin.LogProxy.Warning(
"Privacy cleanup: framework refresh timed out after 5s."
);
_logger.LogWarning("Privacy cleanup: framework refresh timed out after 5s.");
}
WrapperUtil.AddNotification(
@@ -613,7 +612,7 @@ internal sealed class DataManagement : ISettingsTab
}
catch (Exception e)
{
Plugin.LogProxy.Error(e, "Privacy cleanup failed");
_logger.LogError(e, "Privacy cleanup failed");
WrapperUtil.AddNotification(HellionStrings.Cleanup_Error, NotificationType.Error);
}
finally
@@ -773,7 +772,7 @@ internal sealed class DataManagement : ISettingsTab
}
catch (Exception e)
{
Plugin.LogProxy.Error(e, "Export failed");
_logger.LogError(e, "Export failed");
WrapperUtil.AddNotification(HellionStrings.Export_Error, NotificationType.Error);
}
finally
@@ -853,7 +852,7 @@ internal sealed class DataManagement : ISettingsTab
)
)
{
Plugin.LogProxy.Warning("Clearing messages from database");
_logger.LogWarning("Clearing messages from database");
Plugin.MessageManager.Store.ClearMessages();
Plugin.MessageManager.ClearAllTabs();
@@ -911,7 +910,7 @@ internal sealed class DataManagement : ISettingsTab
private void InsertMessages(int count)
{
Plugin.LogProxy.Info($"Inserting {count} messages due to user request");
_logger.LogInformation($"Inserting {count} messages due to user request");
var stopwatch = Stopwatch.StartNew();
var playerName = Plugin.PlayerState.CharacterName;
@@ -956,7 +955,7 @@ internal sealed class DataManagement : ISettingsTab
var elapsedTicks = stopwatch.ElapsedTicks;
stopwatch.Stop();
Plugin.LogProxy.Info(
_logger.LogInformation(
$"Crafted {count} messages in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)"
);
@@ -966,7 +965,7 @@ internal sealed class DataManagement : ISettingsTab
elapsedTicks = stopwatch.ElapsedTicks;
stopwatch.Stop();
Plugin.LogProxy.Info(
_logger.LogInformation(
$"Upserted {count} messages in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)"
);
@@ -977,7 +976,7 @@ internal sealed class DataManagement : ISettingsTab
Plugin.MessageManager.ClearAllTabs();
elapsedTicks = stopwatch.ElapsedTicks;
stopwatch.Stop();
Plugin.LogProxy.Info(
_logger.LogInformation(
$"Cleared {Plugin.Config.Tabs.Count} tabs in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)"
);
})
@@ -990,7 +989,7 @@ internal sealed class DataManagement : ISettingsTab
Plugin.MessageManager.FilterAllTabs();
elapsedTicks = stopwatch.ElapsedTicks;
stopwatch.Stop();
Plugin.LogProxy.Info(
_logger.LogInformation(
$"Fetched and filtered all tabs in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)"
);
})
@@ -7,6 +7,7 @@ using Dalamud.Interface.Utility.Raii;
using HellionChat.Code;
using HellionChat.Resources;
using HellionChat.Util;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui.SettingsTabs;
@@ -14,14 +15,16 @@ internal sealed class FontsAndColours : ISettingsTab
{
private Plugin Plugin { get; }
private Configuration Mutable { get; }
private readonly ILogger<FontsAndColours> _logger;
public string Name =>
HellionStrings.Settings_Card_FontsAndColours_Title + "###tabs-fontsandcolours";
internal FontsAndColours(Plugin plugin, Configuration mutable)
internal FontsAndColours(Plugin plugin, Configuration mutable, ILogger<FontsAndColours> logger)
{
Plugin = plugin;
Mutable = mutable;
_logger = logger;
}
public void Draw(bool changed)
@@ -312,6 +315,6 @@ internal sealed class FontsAndColours : ISettingsTab
}
Plugin.SaveConfig();
GlobalParametersCache.Refresh();
Plugin.LogProxy.Debug($"Applied chat colour preset: {preset.DisplayName}");
_logger.LogDebug($"Applied chat colour preset: {preset.DisplayName}");
}
}
@@ -4,6 +4,7 @@ using Dalamud.Interface.Utility.Raii;
using HellionChat.Resources;
using HellionChat.Themes;
using HellionChat.Util;
using Microsoft.Extensions.Logging;
namespace HellionChat.Ui.SettingsTabs;
@@ -11,16 +12,18 @@ internal sealed class ThemeAndLayout : ISettingsTab
{
private Plugin Plugin { get; }
private Configuration Mutable { get; }
private readonly ILogger<ThemeAndLayout> _logger;
private string? _applyDismissedFor;
public string Name =>
HellionStrings.Settings_Card_ThemeAndLayout_Title + "###tabs-themeandlayout";
internal ThemeAndLayout(Plugin plugin, Configuration mutable)
internal ThemeAndLayout(Plugin plugin, Configuration mutable, ILogger<ThemeAndLayout> logger)
{
Plugin = plugin;
Mutable = mutable;
_logger = logger;
}
public void Draw(bool changed)
@@ -90,7 +93,7 @@ internal sealed class ThemeAndLayout : ISettingsTab
var path = Path.Combine(dir, fileName);
var json = ThemeJsonWriter.Serialize(active);
File.WriteAllText(path, json);
Plugin.LogProxy.Information($"Exported active theme '{active.Slug}' to {path}");
_logger.LogInformation($"Exported active theme '{active.Slug}' to {path}");
}
}
}