diff --git a/windows/PopupWindow.xaml.cs b/windows/PopupWindow.xaml.cs index 34bcf1a..ec1c2eb 100644 --- a/windows/PopupWindow.xaml.cs +++ b/windows/PopupWindow.xaml.cs @@ -1,11 +1,7 @@ using ClaudeCheckerWindows.Controls; using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; -using System.Windows.Input; using System.Windows.Media; using System.Windows.Threading; @@ -20,7 +16,6 @@ public partial class PopupWindow : Window public PopupWindow() { InitializeComponent(); - DataContext = VM; VM.PropertyChanged += (_, e) => Dispatcher.InvokeAsync(() => OnVmChanged(e.PropertyName)); Updater.PropertyChanged += (_, e) => Dispatcher.InvokeAsync(() => OnUpdaterChanged(e.PropertyName)); @@ -37,7 +32,7 @@ public partial class PopupWindow : Window Deactivated += (_, _) => Hide(); } - // ── Card rendering ────────────────────────────────────────────── + // ── Card rendering ─────────────────────────────────────────────── private void RebuildCards() { @@ -48,144 +43,107 @@ public partial class PopupWindow : Window private static UIElement BuildCard(AgentLimit limit) { - var accent = new SolidColorBrush(Color.FromRgb(0xF9, 0x73, 0x16)); + var accent = new SolidColorBrush(Color.FromRgb(0xF9, 0x73, 0x16)); + var secondary = (SolidColorBrush)Application.Current.Resources["SecondaryBrush"]; - var gauge = new GaugeControl + // Gauge + var gauge = new GaugeControl { Width = 76, Height = 76, Percent = limit.UsedPercent, Accent = accent }; + + // Live badge + var liveBadge = new Border { - Width = 76, Height = 76, Percent = limit.UsedPercent, Accent = accent, + Background = new SolidColorBrush(Color.FromArgb(26, 0, 200, 0)), + CornerRadius = new CornerRadius(4), + Padding = new Thickness(6, 2, 6, 2), + Margin = new Thickness(0, 0, 6, 0), + Visibility = limit.IsLive ? Visibility.Visible : Visibility.Collapsed, + Child = new TextBlock { Text = "Live", FontSize = 10, FontWeight = FontWeights.Medium, + Foreground = new SolidColorBrush(Colors.LightGreen) } }; - var livePanel = new StackPanel { Orientation = Orientation.Horizontal }; - if (limit.IsLive) - { - livePanel.Children.Add(new Border - { - Background = new SolidColorBrush(Color.FromArgb(26, 0, 255, 0)), - CornerRadius = new CornerRadius(4), - Padding = new Thickness(6, 2, 6, 2), - Margin = new Thickness(0, 0, 6, 0), - Child = new TextBlock - { - Text = "Live", - FontSize = 10, - FontWeight = FontWeights.Medium, - Foreground = new SolidColorBrush(Colors.LightGreen), - } - }); - } - + // Usage badge var usageBadge = new Border { - Background = new SolidColorBrush(Color.FromArgb(15, 255, 255, 255)), - CornerRadius = new CornerRadius(4), - Padding = new Thickness(6, 2, 6, 2), - Child = new TextBlock - { - Text = $"Usg: {limit.UsageLabel}", - FontSize = 10, - FontWeight = FontWeights.Medium, - Foreground = (SolidColorBrush)Application.Current.Resources["SecondaryBrush"], - } - }; - livePanel.Children.Add(usageBadge); - - var progressBar = new ProgressBar - { - Value = limit.UsedPercent, - Maximum = 100, - Height = 4, - Foreground = accent, - Background = new SolidColorBrush(Color.FromArgb(18, 255, 255, 255)), - BorderThickness = new Thickness(0), - Margin = new Thickness(0, 4, 0, 4), + Background = new SolidColorBrush(Color.FromArgb(15, 255, 255, 255)), + CornerRadius = new CornerRadius(4), + Padding = new Thickness(6, 2, 6, 2), + Child = new TextBlock { Text = $"Usg: {limit.UsageLabel}", FontSize = 10, + FontWeight = FontWeights.Medium, Foreground = secondary } }; - var sparkline = new SparklineControl - { - Height = 28, - Data = limit.BurnHistory.Count > 0 ? limit.BurnHistory : null, - LineColor = Color.FromRgb(0xF9, 0x73, 0x16), - Margin = new Thickness(0, 8, 0, 0), - }; + // Header row: Claude · Pro | [Live] [Usg] + var badgeStack = new StackPanel { Orientation = Orientation.Horizontal }; + badgeStack.Children.Add(liveBadge); + badgeStack.Children.Add(usageBadge); - var infoPanel = new StackPanel { VerticalAlignment = VerticalAlignment.Center }; - infoPanel.Children.Add(new Grid - { - Children = - { - new StackPanel - { - Orientation = Orientation.Horizontal, - Children = - { - MakeText("✦", 11, accent), - MakeText("Claude", 13, Brushes.White, FontWeights.SemiBold, new Thickness(6,0,0,0)), - MakeText($"· {VM.PlanLabel}", 11, - (SolidColorBrush)Application.Current.Resources["SecondaryBrush"], - margin: new Thickness(6,0,0,0)), - } - }.Also(s => Grid.SetColumn(s, 0)), - new StackPanel - { - Orientation = Orientation.Horizontal, - HorizontalAlignment = HorizontalAlignment.Right, - Children = { /* live + usg */ }, - }.Also(s => { Grid.SetColumn(s, 1); ((StackPanel)s).Children.Add(livePanel); }), - }.Tap(g => { - var gc = (Grid)g.Parent ?? (Grid)Application.Current.MainWindow; - ((Grid)g.Parent!).ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); - ((Grid)g.Parent!).ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); - }), - }); + var nameStack = new StackPanel { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center }; + nameStack.Children.Add(new TextBlock { Text = "✦", FontSize = 11, Foreground = accent, VerticalAlignment = VerticalAlignment.Center }); + nameStack.Children.Add(new TextBlock { Text = "Claude", FontSize = 13, FontWeight = FontWeights.SemiBold, + Foreground = Brushes.White, Margin = new Thickness(6, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center }); + nameStack.Children.Add(new TextBlock { Text = $"· {VM.PlanLabel}", FontSize = 11, + Foreground = secondary, Margin = new Thickness(6, 0, 0, 0), VerticalAlignment = VerticalAlignment.Center }); - // Simpler row approach var headerRow = new Grid(); headerRow.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); headerRow.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); + Grid.SetColumn(nameStack, 0); + Grid.SetColumn(badgeStack, 1); + headerRow.Children.Add(nameStack); + headerRow.Children.Add(badgeStack); - var leftStack = new StackPanel { Orientation = Orientation.Horizontal }; - leftStack.Children.Add(MakeText("✦", 11, accent)); - leftStack.Children.Add(MakeText("Claude", 13, Brushes.White, FontWeights.SemiBold, new Thickness(6, 0, 0, 0))); - leftStack.Children.Add(MakeText($"· {VM.PlanLabel}", 11, - (SolidColorBrush)Application.Current.Resources["SecondaryBrush"], margin: new Thickness(6, 0, 0, 0))); + // Progress bar + var progress = new ProgressBar + { + Value = limit.UsedPercent, Maximum = 100, Height = 4, + Margin = new Thickness(0, 4, 0, 4), + Foreground = accent, + Background = new SolidColorBrush(Color.FromArgb(18, 255, 255, 255)), + BorderThickness = new Thickness(0), + }; - Grid.SetColumn(leftStack, 0); - Grid.SetColumn(livePanel, 1); - headerRow.Children.Add(leftStack); - headerRow.Children.Add(livePanel); + // Time row + var timeStack = new StackPanel { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center }; + timeStack.Children.Add(new TextBlock { Text = "⏱ ", FontSize = 10, Foreground = secondary, VerticalAlignment = VerticalAlignment.Center }); + timeStack.Children.Add(new TextBlock { Text = limit.TimeRemaining, FontSize = 12, + FontWeight = FontWeights.Medium, Foreground = Brushes.White, VerticalAlignment = VerticalAlignment.Center }); - infoPanel.Children.Clear(); - infoPanel.Children.Add(headerRow); - infoPanel.Children.Add(progressBar); + var resetText = new TextBlock + { + Text = $"Resets {limit.ResetDate:d MMM yyyy} at {limit.ResetDate:HH:mm}", + FontSize = 11, Foreground = secondary, VerticalAlignment = VerticalAlignment.Center + }; var timeRow = new Grid(); timeRow.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); timeRow.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); - - var timeLeft = new StackPanel { Orientation = Orientation.Horizontal }; - timeLeft.Children.Add(MakeText("⏱ ", 10, (SolidColorBrush)Application.Current.Resources["SecondaryBrush"])); - timeLeft.Children.Add(MakeText(limit.TimeRemaining, 12, Brushes.White, FontWeights.Medium)); - - var resetText = MakeText( - $"Resets {limit.ResetDate:d MMM yyyy} at {limit.ResetDate:HH:mm}", - 11, (SolidColorBrush)Application.Current.Resources["SecondaryBrush"]); - - Grid.SetColumn(timeLeft, 0); - Grid.SetColumn(resetText, 1); - timeRow.Children.Add(timeLeft); + Grid.SetColumn(timeStack, 0); + Grid.SetColumn(resetText, 1); + timeRow.Children.Add(timeStack); timeRow.Children.Add(resetText); + + // Sparkline + var sparkline = new SparklineControl + { + Height = 28, Margin = new Thickness(0, 8, 0, 0), LineColor = Color.FromRgb(0xF9, 0x73, 0x16), + Data = limit.BurnHistory.Count > 0 ? limit.BurnHistory : null, + }; + + // Info panel + var infoPanel = new StackPanel { VerticalAlignment = VerticalAlignment.Center }; + infoPanel.Children.Add(headerRow); + infoPanel.Children.Add(progress); infoPanel.Children.Add(timeRow); infoPanel.Children.Add(sparkline); - var cardContent = new Grid { Margin = new Thickness(16, 12, 16, 12) }; - cardContent.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); - cardContent.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(16, GridUnitType.Pixel) }); - cardContent.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); - - Grid.SetColumn(gauge, 0); + // Card content grid + var cardGrid = new Grid { Margin = new Thickness(16, 12, 16, 12) }; + cardGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); + cardGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(16, GridUnitType.Pixel) }); + cardGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); + Grid.SetColumn(gauge, 0); Grid.SetColumn(infoPanel, 2); - cardContent.Children.Add(gauge); - cardContent.Children.Add(infoPanel); + cardGrid.Children.Add(gauge); + cardGrid.Children.Add(infoPanel); var card = new Border { @@ -193,15 +151,15 @@ public partial class PopupWindow : Window CornerRadius = new CornerRadius(8), BorderBrush = new SolidColorBrush(Color.FromArgb(20, 255, 255, 255)), BorderThickness = new Thickness(0.5), - Child = cardContent, Margin = new Thickness(8, 0, 8, 0), + Child = cardGrid, }; var sectionLabel = new TextBlock { - Text = limit.WindowLabel, - Style = (Style)Application.Current.Resources["HeaderText"], - Margin = new Thickness(16, 10, 16, 4), + Text = limit.WindowLabel, + Style = (Style)Application.Current.Resources["HeaderText"], + Margin = new Thickness(16, 10, 16, 4), }; var section = new StackPanel(); @@ -210,18 +168,6 @@ public partial class PopupWindow : Window return section; } - private static TextBlock MakeText(string text, double size, Brush fg, - FontWeight? weight = null, Thickness? margin = null) - => new() - { - Text = text, - FontSize = size, - Foreground = fg, - FontWeight = weight ?? FontWeights.Normal, - Margin = margin ?? new Thickness(0), - VerticalAlignment = VerticalAlignment.Center, - }; - // ── State updates ──────────────────────────────────────────────── private void OnVmChanged(string? prop) @@ -259,9 +205,8 @@ public partial class PopupWindow : Window if (VM.LastUpdated.HasValue) { var diff = DateTime.Now - VM.LastUpdated.Value; - LastUpdatedText.Text = diff.TotalSeconds < 10 - ? "Updated just now" - : $"Updated {(int)diff.TotalSeconds}s ago"; + LastUpdatedText.Text = diff.TotalSeconds < 10 ? "Updated just now" + : $"Updated {(int)diff.TotalSeconds}s ago"; } else { @@ -274,45 +219,37 @@ public partial class PopupWindow : Window private void InitSettings() { - VersionLabel.Text = $"v{App.Updater.CurrentVersion}"; + VersionLabel.Text = $"v{Updater.CurrentVersion}"; BetaToggle.IsChecked = Updater.BetaChannel; CheckUpdateButton.Content = Updater.UpdateAvailable ? "Install" : "Check"; - RefreshPicker.ItemsSource = new[] - { - ("1 min", 60), ("2 min", 120), ("3 min", 180), - ("4 min", 240), ("5 min", 300), ("10 min", 600), - }; - RefreshPicker.DisplayMemberPath = "Item1"; - RefreshPicker.SelectedIndex = Array.FindIndex( - new[] { 60, 120, 180, 240, 300, 600 }, - s => s == VM.RefreshInterval).Let(i => i < 0 ? 1 : i); + var intervals = new[] { ("1 min", 60), ("2 min", 120), ("3 min", 180), + ("4 min", 240), ("5 min", 300), ("10 min", 600) }; + RefreshPicker.ItemsSource = intervals; + RefreshPicker.DisplayMemberPath = "Item1"; + var idx = Array.FindIndex(intervals, x => x.Item2 == VM.RefreshInterval); + RefreshPicker.SelectedIndex = idx < 0 ? 1 : idx; - var signedIn = VM.IsSignedIn; AuthPanel.Children.Clear(); - AuthPanel.Children.Add(new StackPanel + var dot = new System.Windows.Shapes.Ellipse { - Orientation = Orientation.Horizontal, - Children = - { - new Ellipse - { - Width = 7, Height = 7, - Fill = signedIn ? Brushes.LimeGreen : Brushes.Orange, - Margin = new Thickness(0,0,6,0), - VerticalAlignment = VerticalAlignment.Center, - }, - new TextBlock - { - Text = signedIn ? $"Signed in — {VM.UserEmail}" : "Not signed in", - FontSize = 13, FontWeight = FontWeights.SemiBold, Foreground = Brushes.White, - VerticalAlignment = VerticalAlignment.Center, - } - } - }); + Width = 7, Height = 7, Margin = new Thickness(0, 0, 6, 0), + Fill = VM.IsSignedIn ? Brushes.LimeGreen : Brushes.Orange, + VerticalAlignment = VerticalAlignment.Center, + }; + var label = new TextBlock + { + Text = VM.IsSignedIn ? $"Signed in — {VM.UserEmail}" : "Not signed in", + FontSize = 13, FontWeight = FontWeights.SemiBold, Foreground = Brushes.White, + VerticalAlignment = VerticalAlignment.Center, + }; + var authRow = new StackPanel { Orientation = Orientation.Horizontal }; + authRow.Children.Add(dot); + authRow.Children.Add(label); + AuthPanel.Children.Add(authRow); - SignOutButton.Visibility = signedIn ? Visibility.Visible : Visibility.Collapsed; - SignInButton.Content = signedIn ? "Re-authenticate" : "Sign In"; + SignOutButton.Visibility = VM.IsSignedIn ? Visibility.Visible : Visibility.Collapsed; + SignInButton.Content = VM.IsSignedIn ? "Re-authenticate" : "Sign In"; } // ── Event handlers ─────────────────────────────────────────────── @@ -320,18 +257,18 @@ public partial class PopupWindow : Window private void Settings_Click(object s, RoutedEventArgs e) { InitSettings(); - MainPanel.Visibility = Visibility.Collapsed; + MainPanel.Visibility = Visibility.Collapsed; SettingsPanel.Visibility = Visibility.Visible; - UpdatePanel.Visibility = Visibility.Collapsed; + UpdatePanel.Visibility = Visibility.Collapsed; } private void BackFromSettings_Click(object s, RoutedEventArgs e) => ShowMain(); private void ShowMain() { - MainPanel.Visibility = Visibility.Visible; + MainPanel.Visibility = Visibility.Visible; SettingsPanel.Visibility = Visibility.Collapsed; - UpdatePanel.Visibility = Visibility.Collapsed; + UpdatePanel.Visibility = Visibility.Collapsed; } private void Close_Click(object s, RoutedEventArgs e) => Hide(); @@ -342,19 +279,19 @@ public partial class PopupWindow : Window private void ShowUpdatePanel() { - CurrentVerLabel.Text = $"v{Updater.CurrentVersion}"; - NewVerLabel.Text = $"v{Updater.LatestVersion}"; - ReleaseNotesText.Text = Updater.ReleaseNotes; - MainPanel.Visibility = Visibility.Collapsed; + CurrentVerLabel.Text = $"v{Updater.CurrentVersion}"; + NewVerLabel.Text = $"v{Updater.LatestVersion}"; + ReleaseNotesText.Text = Updater.ReleaseNotes; + MainPanel.Visibility = Visibility.Collapsed; SettingsPanel.Visibility = Visibility.Collapsed; - UpdatePanel.Visibility = Visibility.Visible; + UpdatePanel.Visibility = Visibility.Visible; } private void CloseUpdate_Click(object s, RoutedEventArgs e) => ShowMain(); private async void Install_Click(object s, RoutedEventArgs e) { - NotNowButton.Visibility = Visibility.Collapsed; + NotNowButton.Visibility = Visibility.Collapsed; UpdateStatusText.Visibility = Visibility.Visible; UpdateProgress.Visibility = Visibility.Visible; await Updater.DownloadAndInstallAsync(); @@ -362,8 +299,7 @@ public partial class PopupWindow : Window private void SignIn_Click(object s, RoutedEventArgs e) { - var login = new LoginWindow(); - login.Owner = this; + var login = new LoginWindow { Owner = this }; if (login.ShowDialog() == true) { _ = VM.RefreshAsync(); @@ -385,10 +321,10 @@ public partial class PopupWindow : Window private void RefreshPicker_Changed(object s, SelectionChangedEventArgs e) { - if (RefreshPicker.SelectedItem is (string _, int seconds)) + if (RefreshPicker.SelectedItem is ValueTuple selected) { - VM.RefreshInterval = seconds; - ((App)Application.Current).ScheduleTimer(seconds); + VM.RefreshInterval = selected.Item2; + ((App)Application.Current).ScheduleTimer(selected.Item2); } } @@ -400,12 +336,3 @@ public partial class PopupWindow : Window CheckUpdateButton.Content = Updater.UpdateAvailable ? "Install" : "Check"; } } - -// Minimal extension helpers to avoid repeating boilerplate -file static class Extensions -{ - public static T Also(this T t, Action f) { f(t); return t; } - public static TResult Let(this T t, Func f) => f(t); -} - -file class Ellipse : System.Windows.Shapes.Ellipse { }