Skip to content

Commit 3df3889

Browse files
committed
feat: add prompt editor, backup manager, and history restoration
- Add PromptEditorWindow for editing prompts before sending - Implement OptimizationBackupManager for service/task/autorun backups - Extend ActionHistory with state restoration capabilities - Add language fallback and missing translation detection - Introduce interaction animation decorator for UI enhancements - Update language files with new keys for backup and history features - Improve history window with better filtering and restore functionality
1 parent 656e8df commit 3df3889

12 files changed

Lines changed: 2084 additions & 95 deletions

src/ActionHistory.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public enum ActionType
1919
ServiceManual, // Service startup changed to manual
2020
TaskDisabled, // Task was disabled
2121
AutorunDisabled, // Autorun was disabled
22+
StateRestored, // Previous state restored from history
2223
Snooze // Notification was snoozed
2324
}
2425

@@ -37,11 +38,14 @@ public class ActionHistoryEntry
3738
public double? GpuUsage { get; set; } // If applicable
3839
public string Reason { get; set; } // Why the action was taken
3940
public string Source { get; set; } // "GameMode", "User", "AI", etc.
41+
public string PreviousStateJson { get; set; } // State before change
42+
public string AppliedStateJson { get; set; } // State after change
4043

4144
// Formatted display properties
4245
public string TimeAgo => GetTimeAgo(Timestamp);
4346
public string ActionIcon => GetActionIcon(Action);
4447
public string ActionDescription => GetActionDescription(Action, TargetName, Source);
48+
public string ChangeSummary => BuildChangeSummary();
4549

4650
private string GetTimeAgo(DateTime time)
4751
{
@@ -63,6 +67,7 @@ private string GetActionIcon(ActionType action)
6367
ActionType.ServiceManual => "⚙️",
6468
ActionType.TaskDisabled => "📅",
6569
ActionType.AutorunDisabled => "🚀",
70+
ActionType.StateRestored => "↩️",
6671
ActionType.Snooze => "😴",
6772
_ => "⚡"
6873
};
@@ -79,13 +84,29 @@ private string GetActionDescription(ActionType action, string targetName, string
7984
ActionType.ServiceManual => "Set service to manual",
8085
ActionType.TaskDisabled => "Disabled task",
8186
ActionType.AutorunDisabled => "Disabled autorun",
87+
ActionType.StateRestored => "Restored state for",
8288
ActionType.Snooze => "Snoozed",
8389
_ => "Action"
8490
};
8591

8692
var sourceText = string.IsNullOrEmpty(source) ? "" : $" ({source})";
8793
return $"{actionText} {targetName}{sourceText}";
8894
}
95+
96+
private string BuildChangeSummary()
97+
{
98+
if (!string.IsNullOrWhiteSpace(PreviousStateJson) && !string.IsNullOrWhiteSpace(AppliedStateJson))
99+
{
100+
return $"{PreviousStateJson} -> {AppliedStateJson}";
101+
}
102+
103+
if (!string.IsNullOrWhiteSpace(AppliedStateJson))
104+
{
105+
return AppliedStateJson;
106+
}
107+
108+
return string.Empty;
109+
}
89110
}
90111

91112
/// <summary>
@@ -113,7 +134,7 @@ static ActionHistory()
113134

114135
public static void Record(ActionType action, string targetType, string targetName,
115136
string targetPath = "", int? processId = null, double? gpuUsage = null,
116-
string reason = "", string source = "")
137+
string reason = "", string source = "", string previousStateJson = "", string appliedStateJson = "")
117138
{
118139
var entry = new ActionHistoryEntry
119140
{
@@ -124,7 +145,9 @@ public static void Record(ActionType action, string targetType, string targetNam
124145
ProcessId = processId,
125146
GpuUsage = gpuUsage,
126147
Reason = reason,
127-
Source = source
148+
Source = source,
149+
PreviousStateJson = previousStateJson,
150+
AppliedStateJson = appliedStateJson
128151
};
129152

130153
_entries.Insert(0, entry); // Add to top (newest first)

src/HistoryWindow.xaml

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,17 @@
2121
<Setter Property="Foreground" Value="White"/>
2222
<Setter Property="BorderThickness" Value="0"/>
2323
<Setter Property="Padding" Value="16,10"/>
24+
<Setter Property="MinHeight" Value="32"/>
25+
<Setter Property="HorizontalContentAlignment" Value="Center"/>
26+
<Setter Property="VerticalContentAlignment" Value="Center"/>
2427
<Setter Property="FontSize" Value="14"/>
2528
<Setter Property="FontWeight" Value="SemiBold"/>
2629
<Setter Property="Cursor" Value="Hand"/>
2730
<Setter Property="Template">
2831
<Setter.Value>
2932
<ControlTemplate TargetType="Button">
30-
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
31-
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
33+
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
34+
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" Margin="1,0"/>
3235
</Border>
3336
</ControlTemplate>
3437
</Setter.Value>
@@ -40,6 +43,9 @@
4043
<Setter Property="Foreground" Value="{StaticResource TextBrush}"/>
4144
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}"/>
4245
<Setter Property="BorderThickness" Value="1"/>
46+
<Setter Property="Padding" Value="12,8"/>
47+
<Setter Property="TextBlock.TextWrapping" Value="NoWrap"/>
48+
<Setter Property="TextBlock.TextTrimming" Value="CharacterEllipsis"/>
4349
</Style>
4450

4551
<Style x:Key="DangerButtonStyle" TargetType="Button" BasedOn="{StaticResource ModernButtonStyle}">
@@ -82,7 +88,7 @@
8288

8389
<!-- Header -->
8490
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,15">
85-
<TextBlock Text="📜 Action History" FontSize="22" FontWeight="Bold"
91+
<TextBlock x:Name="HistoryTitleText" Text="📜 Action History" FontSize="22" FontWeight="Bold"
8692
Foreground="{StaticResource TextBrush}"/>
8793
<TextBlock x:Name="CountText" Text="(0 actions)" FontSize="14"
8894
Foreground="{StaticResource SecondaryTextBrush}" Margin="15,0,0,0" VerticalAlignment="Bottom"/>
@@ -101,15 +107,18 @@
101107

102108
<!-- Filters -->
103109
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,0,0,10">
104-
<TextBlock Text="Filter:" Foreground="{StaticResource SecondaryTextBrush}"
110+
<TextBlock x:Name="FilterLabelText" Text="Filter:" Foreground="{StaticResource SecondaryTextBrush}"
105111
VerticalAlignment="Center" Margin="0,0,10,0"/>
106112
<ComboBox x:Name="TypeFilterComboBox" Width="150" VerticalAlignment="Center"
107113
SelectionChanged="TypeFilterComboBox_SelectionChanged">
108-
<ComboBoxItem Content="All Actions" IsSelected="True"/>
109-
<ComboBoxItem Content="🤖 AutoKill"/>
110-
<ComboBoxItem Content="🔴 Manual Kill"/>
111-
<ComboBoxItem Content="🛡️ Whitelist"/>
112-
<ComboBoxItem Content="😴 Snooze"/>
114+
<ComboBoxItem Content="All Actions" Tag="all" IsSelected="True"/>
115+
<ComboBoxItem Content="🤖 AutoKill" Tag="autokill"/>
116+
<ComboBoxItem Content="🔴 Manual Kill" Tag="manualkill"/>
117+
<ComboBoxItem Content="🛡️ Whitelist" Tag="whitelist"/>
118+
<ComboBoxItem Content="⚙️ Service Changes" Tag="service"/>
119+
<ComboBoxItem Content="📅 Task Changes" Tag="task"/>
120+
<ComboBoxItem Content="🚀 Autorun Changes" Tag="autorun"/>
121+
<ComboBoxItem Content="😴 Snooze" Tag="snooze"/>
113122
</ComboBox>
114123
<TextBox x:Name="SearchTextBox" Width="200" Margin="15,0,0,0"
115124
VerticalAlignment="Center" TextChanged="SearchTextBox_TextChanged"
@@ -143,7 +152,15 @@
143152
</Style>
144153
</DataGridTextColumn.ElementStyle>
145154
</DataGridTextColumn>
146-
<DataGridTemplateColumn Header="Actions" Width="130">
155+
<DataGridTextColumn Header="Changes" Binding="{Binding ChangeSummary}" Width="240">
156+
<DataGridTextColumn.ElementStyle>
157+
<Style TargetType="TextBlock">
158+
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
159+
<Setter Property="ToolTip" Value="{Binding ChangeSummary}"/>
160+
</Style>
161+
</DataGridTextColumn.ElementStyle>
162+
</DataGridTextColumn>
163+
<DataGridTemplateColumn Header="Actions" Width="170">
147164
<DataGridTemplateColumn.CellTemplate>
148165
<DataTemplate>
149166
<StackPanel Orientation="Horizontal">
@@ -159,6 +176,10 @@
159176
Click="BtnKillAgain_Click"
160177
Style="{StaticResource ModernButtonStyle}"
161178
Padding="8,4" FontSize="11" Margin="0,0,6,0"/>
179+
<Button Content="" ToolTip="Restore This Change"
180+
Click="BtnRestoreFromHistory_Click"
181+
Style="{StaticResource SecondaryButtonStyle}"
182+
Padding="8,4" FontSize="11"/>
162183
</StackPanel>
163184
</DataTemplate>
164185
</DataGridTemplateColumn.CellTemplate>

src/HistoryWindow.xaml.cs

Lines changed: 132 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Windows;
77
using System.Windows.Controls;
8+
using Newtonsoft.Json.Linq;
89

910
namespace WinOptimizer.AI
1011
{
@@ -13,6 +14,7 @@ public partial class HistoryWindow : Window
1314
public HistoryWindow()
1415
{
1516
InitializeComponent();
17+
ApplyLanguage();
1618
LoadHistory();
1719

1820
// Subscribe to new entries to auto-refresh
@@ -58,13 +60,16 @@ private void ApplyFilters()
5860
// Filter by action type
5961
if (TypeFilterComboBox.SelectedItem is ComboBoxItem item)
6062
{
61-
var selected = item.Content.ToString();
63+
var selected = (item.Tag?.ToString() ?? "all").ToLowerInvariant();
6264
filtered = selected switch
6365
{
64-
"🤖 AutoKill" => filtered.Where(e => e.Action == ActionType.AutoKill),
65-
"🔴 Manual Kill" => filtered.Where(e => e.Action == ActionType.ManualKill),
66-
"🛡️ Whitelist" => filtered.Where(e => e.Action == ActionType.WhitelistAdd),
67-
"😴 Snooze" => filtered.Where(e => e.Action == ActionType.Snooze),
66+
"autokill" => filtered.Where(e => e.Action == ActionType.AutoKill),
67+
"manualkill" => filtered.Where(e => e.Action == ActionType.ManualKill),
68+
"whitelist" => filtered.Where(e => e.Action == ActionType.WhitelistAdd),
69+
"service" => filtered.Where(e => e.TargetType == "Service"),
70+
"task" => filtered.Where(e => e.TargetType == "Task"),
71+
"autorun" => filtered.Where(e => e.TargetType == "Autorun"),
72+
"snooze" => filtered.Where(e => e.Action == ActionType.Snooze),
6873
_ => filtered
6974
};
7075
}
@@ -163,6 +168,128 @@ private void BtnKillAgain_Click(object sender, RoutedEventArgs e)
163168
}
164169
}
165170

171+
private void BtnRestoreFromHistory_Click(object sender, RoutedEventArgs e)
172+
{
173+
if (sender is not Button btn || btn.DataContext is not ActionHistoryEntry entry)
174+
{
175+
return;
176+
}
177+
178+
if (string.IsNullOrWhiteSpace(entry.PreviousStateJson))
179+
{
180+
MessageBox.Show("No previous state stored for this entry.", "Restore", MessageBoxButton.OK, MessageBoxImage.Information);
181+
return;
182+
}
183+
184+
try
185+
{
186+
switch (entry.TargetType)
187+
{
188+
case "Service":
189+
RestoreServiceFromHistory(entry);
190+
break;
191+
case "Task":
192+
RestoreTaskFromHistory(entry);
193+
break;
194+
case "Autorun":
195+
RestoreAutorunFromHistory(entry);
196+
break;
197+
default:
198+
MessageBox.Show("Restore is available only for Service/Task/Autorun entries.", "Restore", MessageBoxButton.OK, MessageBoxImage.Information);
199+
return;
200+
}
201+
202+
ActionHistory.Record(ActionType.StateRestored, entry.TargetType, entry.TargetName, entry.TargetPath,
203+
reason: "Restored from history", source: "History",
204+
previousStateJson: entry.AppliedStateJson, appliedStateJson: entry.PreviousStateJson);
205+
206+
LoadHistory();
207+
MessageBox.Show($"State restored for '{entry.TargetName}'.", "Restore", MessageBoxButton.OK, MessageBoxImage.Information);
208+
}
209+
catch (Exception ex)
210+
{
211+
MessageBox.Show($"Restore failed: {ex.Message}", "Restore Error", MessageBoxButton.OK, MessageBoxImage.Error);
212+
}
213+
}
214+
215+
private static void RestoreServiceFromHistory(ActionHistoryEntry entry)
216+
{
217+
var state = JObject.Parse(entry.PreviousStateJson);
218+
var startup = state["Startup"]?.ToString() ?? "Manual";
219+
var status = state["State"]?.ToString() ?? "Stopped";
220+
221+
var backupEntry = new ServiceBackupEntry
222+
{
223+
ServiceName = entry.TargetName,
224+
StartupType = startup.ToLowerInvariant() switch
225+
{
226+
"automatic" => 2,
227+
"delayed" => 2,
228+
"disabled" => 4,
229+
_ => 3
230+
},
231+
IsRunning = status.Equals("Running", StringComparison.OrdinalIgnoreCase)
232+
};
233+
234+
OptimizationBackupManager.RestoreService(backupEntry);
235+
}
236+
237+
private static void RestoreTaskFromHistory(ActionHistoryEntry entry)
238+
{
239+
var state = JObject.Parse(entry.PreviousStateJson);
240+
var isEnabled = state["Enabled"]?.Value<bool>() ?? true;
241+
var taskPath = string.IsNullOrWhiteSpace(entry.TargetPath) ? entry.TargetName : entry.TargetPath;
242+
243+
var backupEntry = new TaskBackupEntry
244+
{
245+
TaskPath = taskPath,
246+
IsEnabled = isEnabled
247+
};
248+
249+
OptimizationBackupManager.RestoreTask(backupEntry);
250+
}
251+
252+
private static void RestoreAutorunFromHistory(ActionHistoryEntry entry)
253+
{
254+
var state = JObject.Parse(entry.PreviousStateJson);
255+
var present = state["Present"]?.Value<bool>() ?? false;
256+
if (!present)
257+
{
258+
throw new InvalidOperationException("Previous state indicates this autorun entry did not exist.");
259+
}
260+
261+
var backupEntry = new AutorunBackupEntry
262+
{
263+
Location = state["Location"]?.ToString(),
264+
EntryName = state["Name"]?.ToString() ?? entry.TargetName,
265+
Command = state["Command"]?.ToString()
266+
};
267+
268+
OptimizationBackupManager.RestoreAutorun(backupEntry);
269+
}
270+
271+
private void ApplyLanguage()
272+
{
273+
Title = LanguageManager.GetString("HistoryTitle");
274+
HistoryTitleText.Text = LanguageManager.GetString("HistoryHeader");
275+
FilterLabelText.Text = LanguageManager.GetString("FilterLabel");
276+
BtnExport.Content = LanguageManager.GetString("BtnExportHistoryJson");
277+
BtnClearAll.Content = LanguageManager.GetString("BtnClearHistory");
278+
BtnClose.Content = LanguageManager.GetString("PromptEditorBtnClose");
279+
280+
if (TypeFilterComboBox.Items.Count >= 8)
281+
{
282+
((ComboBoxItem)TypeFilterComboBox.Items[0]).Content = LanguageManager.GetString("FilterAllActions");
283+
((ComboBoxItem)TypeFilterComboBox.Items[1]).Content = LanguageManager.GetString("FilterAutoKill");
284+
((ComboBoxItem)TypeFilterComboBox.Items[2]).Content = LanguageManager.GetString("FilterManualKill");
285+
((ComboBoxItem)TypeFilterComboBox.Items[3]).Content = LanguageManager.GetString("FilterWhitelist");
286+
((ComboBoxItem)TypeFilterComboBox.Items[4]).Content = LanguageManager.GetString("FilterServiceChanges");
287+
((ComboBoxItem)TypeFilterComboBox.Items[5]).Content = LanguageManager.GetString("FilterTaskChanges");
288+
((ComboBoxItem)TypeFilterComboBox.Items[6]).Content = LanguageManager.GetString("FilterAutorunChanges");
289+
((ComboBoxItem)TypeFilterComboBox.Items[7]).Content = LanguageManager.GetString("FilterSnooze");
290+
}
291+
}
292+
166293
private void BtnExport_Click(object sender, RoutedEventArgs e)
167294
{
168295
try

0 commit comments

Comments
 (0)