diff --git a/ChatTwo/Resources/HellionStrings.Designer.cs b/ChatTwo/Resources/HellionStrings.Designer.cs
index 671109f..61ead5b 100644
--- a/ChatTwo/Resources/HellionStrings.Designer.cs
+++ b/ChatTwo/Resources/HellionStrings.Designer.cs
@@ -64,6 +64,7 @@ internal class HellionStrings
internal static string Cleanup_Heading => Get(nameof(Cleanup_Heading));
internal static string Cleanup_Help_Intro => Get(nameof(Cleanup_Help_Intro));
internal static string Cleanup_Help_SavedNote => Get(nameof(Cleanup_Help_SavedNote));
+ internal static string Cleanup_Preview_Stale => Get(nameof(Cleanup_Preview_Stale));
internal static string Retention_Help_SavedNote => Get(nameof(Retention_Help_SavedNote));
internal static string Cleanup_RefreshPreview => Get(nameof(Cleanup_RefreshPreview));
internal static string Cleanup_NoPreview => Get(nameof(Cleanup_NoPreview));
diff --git a/ChatTwo/Resources/HellionStrings.de.resx b/ChatTwo/Resources/HellionStrings.de.resx
index d82ef5a..2a586ae 100644
--- a/ChatTwo/Resources/HellionStrings.de.resx
+++ b/ChatTwo/Resources/HellionStrings.de.resx
@@ -81,6 +81,9 @@
Der manuelle Lauf nutzt deine GESPEICHERTE Retention-Policy, nicht die Slider-Werte oben. Klicke zuerst Speichern, wenn der Lauf deine aktuellen Änderungen anwenden soll.
+
+ Vorschau veraltet, deine Whitelist hat sich seit dem letzten Aktualisieren geändert. Klicke Aktualisieren, um neu zu berechnen.
+
Vorschau aktualisieren
diff --git a/ChatTwo/Resources/HellionStrings.resx b/ChatTwo/Resources/HellionStrings.resx
index 6617440..817c3c7 100644
--- a/ChatTwo/Resources/HellionStrings.resx
+++ b/ChatTwo/Resources/HellionStrings.resx
@@ -81,6 +81,9 @@
The manual sweep uses your SAVED retention policy, not the slider values above. Click Save first if you want the run to apply your current edits.
+
+ Preview is out of date — your whitelist has changed since the last refresh. Click Refresh to recalculate.
+
Refresh preview
diff --git a/ChatTwo/Ui/SettingsTabs/Privacy.cs b/ChatTwo/Ui/SettingsTabs/Privacy.cs
index eb3bea3..6126230 100644
--- a/ChatTwo/Ui/SettingsTabs/Privacy.cs
+++ b/ChatTwo/Ui/SettingsTabs/Privacy.cs
@@ -3,6 +3,7 @@ using ChatTwo.Export;
using ChatTwo.Privacy;
using ChatTwo.Resources;
using ChatTwo.Util;
+using Dalamud.Interface.Colors;
using Dalamud.Interface.ImGuiNotification;
using Dalamud.Interface.Utility;
using Dalamud.Interface.Utility.Raii;
@@ -55,6 +56,8 @@ internal sealed class Privacy : ISettingsTab
private long CleanupKeepCount;
private long CleanupDeleteCount;
private bool CleanupRunning;
+ private bool CleanupPreviewStale;
+ private HashSet? CleanupPreviewSnapshot;
// The retention-running state lives on Plugin so the auto-sweep and
// this manual button see the same flag. UI reads stay lock-free
@@ -484,6 +487,21 @@ internal sealed class Privacy : ISettingsTab
ImGui.Spacing();
+ // Drift-detection between the snapshot taken at last refresh
+ // and the current Mutable whitelist. Cleanup itself runs on
+ // the SAVED policy (Cleanup_Help_SavedNote covers that), but
+ // the user usually expects "the preview reflects what I just
+ // ticked" — so we surface the divergence instead of silently
+ // showing stale numbers.
+ if (CleanupPreviewSnapshot is not null
+ && !CleanupPreviewSnapshot.SetEquals(Mutable.PrivacyPersistChannels))
+ {
+ CleanupPreviewStale = true;
+ }
+
+ using (var emphasis = CleanupPreviewStale
+ ? ImRaii.PushColor(ImGuiCol.Button, ImGuiColors.HealerGreen with { W = 0.6f })
+ : null)
using (ImRaii.Disabled(CleanupRunning))
{
if (ImGui.Button(HellionStrings.Cleanup_RefreshPreview))
@@ -496,10 +514,22 @@ internal sealed class Privacy : ISettingsTab
return;
}
+ if (CleanupPreviewStale)
+ {
+ ImGui.Spacing();
+ ImGuiUtil.HelpText(HellionStrings.Cleanup_Preview_Stale);
+ }
+
ImGui.Spacing();
- ImGuiUtil.HelpText(string.Format(HellionStrings.Cleanup_TotalStored, CleanupKeepCount + CleanupDeleteCount));
- ImGuiUtil.HelpText(string.Format(HellionStrings.Cleanup_WillKeep, CleanupKeepCount));
- ImGuiUtil.HelpText(string.Format(HellionStrings.Cleanup_WillDelete, CleanupDeleteCount));
+
+ using (var staleColor = CleanupPreviewStale
+ ? ImRaii.PushColor(ImGuiCol.Text, ImGuiColors.DalamudGrey)
+ : null)
+ {
+ ImGuiUtil.HelpText(string.Format(HellionStrings.Cleanup_TotalStored, CleanupKeepCount + CleanupDeleteCount));
+ ImGuiUtil.HelpText(string.Format(HellionStrings.Cleanup_WillKeep, CleanupKeepCount));
+ ImGuiUtil.HelpText(string.Format(HellionStrings.Cleanup_WillDelete, CleanupDeleteCount));
+ }
using (var tree = ImRaii.TreeNode(HellionStrings.Cleanup_Breakdown))
{
@@ -555,6 +585,13 @@ internal sealed class Privacy : ISettingsTab
else
CleanupDeleteCount += count;
}
+
+ // Snapshot the whitelist as it stood at preview-time so the
+ // render pass can flag the user about subsequent edits. Only
+ // updated on success — if the preview throws, the previous
+ // snapshot stays in place so stale-detection keeps working.
+ CleanupPreviewSnapshot = new HashSet(Mutable.PrivacyPersistChannels);
+ CleanupPreviewStale = false;
}
catch (Exception e)
{