Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/PayBeat.App/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using Microsoft.Win32;
using PayBeat.App.Helpers;
using PayBeat.App.Models;
using PayBeat.App.Services;
using PayBeat.App.ViewModels;
using PayBeat.App.Views;
using Microsoft.Win32;
using System.Windows.Interop;

namespace PayBeat.App;
Expand Down Expand Up @@ -62,6 +62,7 @@ protected override void OnStartup(StartupEventArgs e)
var settings = _settingsService.Load();
_startupSettings = settings;
LocalizationService.Apply(settings.Language);
ThemeService.Apply(settings.Theme);

_singleInstanceMutex = new Mutex(initiallyOwned: true, "PayBeat_SingleInstance", out var createdNew);
if (!createdNew)
Expand Down
1 change: 0 additions & 1 deletion src/PayBeat.App/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
global using System.Windows;
global using System.Windows.Controls;
global using System.Windows.Input;
global using System.Windows.Threading;
global using System.IO;
Expand Down
86 changes: 66 additions & 20 deletions src/PayBeat.App/Models/SalarySettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,44 @@ public record SalarySettings
{
public const decimal MaxDailySalary = 99_999_999m;

/// <summary>Daily gross salary used to compute the per-second earnings rate.</summary>
/// <summary>
/// Daily gross salary used to compute the per-second earnings rate.
/// </summary>
public decimal DailySalary { get; init; } = 500m;

/// <summary>Work day start time; earnings accrue from this moment.</summary>
/// <summary>
/// Work day start time; earnings accrue from this moment.
/// </summary>
public TimeOnly WorkStart { get; init; } = new(9, 0);

/// <summary>Work day end time; earnings are capped at <see cref="DailySalary"/> after this.</summary>
/// <summary>
/// Work day end time; earnings are capped at <see cref="DailySalary"/> after this.
/// </summary>
public TimeOnly WorkEnd { get; init; } = new(18, 0);

/// <summary>Currency symbol prepended to the formatted earnings string.</summary>
/// <summary>
/// Currency symbol prepended to the formatted earnings string.
/// </summary>
public string Currency { get; init; } = "¥";

/// <summary>Active widget display mode, determining which view template is shown.</summary>
/// <summary>
/// Active widget display mode, determining which view template is shown.
/// </summary>
public DisplayMode DisplayMode { get; init; } = DisplayMode.Normal;

/// <summary>Whether the widget window is pinned above all other windows.</summary>
/// <summary>
/// Whether the widget window is pinned above all other windows.
/// </summary>
public bool AlwaysOnTop { get; init; } = true;

/// <summary>Timer tick interval in seconds for refreshing the earnings display.</summary>
/// <summary>
/// Timer tick interval in seconds for refreshing the earnings display.
/// </summary>
public int RefreshInterval { get; init; } = 1;

/// <summary>Window opacity when the mouse is not hovering (0.1–1.0).</summary>
/// <summary>
/// Window opacity when the mouse is not hovering (0.1–1.0).
/// </summary>
public double Opacity { get; init; } = 1.0;

/// <summary>
Expand All @@ -44,48 +60,78 @@ public record SalarySettings
/// </summary>
public int HotkeyModifiers { get; init; } = 0x0003;

/// <summary>Virtual-key code for the global show/hide hotkey. Default <c>0x58</c> = X.</summary>
/// <summary>
/// Virtual-key code for the global show/hide hotkey. Default <c>0x58</c> = X.
/// </summary>
public int HotkeyVirtualKey { get; init; } = 0x58;

/// <summary>Last saved position for <see cref="DisplayMode.Normal"/> mode.</summary>
/// <summary>
/// Last saved position for <see cref="DisplayMode.Normal"/> mode.
/// </summary>
public WindowPosition? NormalPosition
{
get; init;
}

/// <summary>Last saved position for <see cref="DisplayMode.Mini"/> mode.</summary>
/// <summary>
/// Last saved position for <see cref="DisplayMode.Mini"/> mode.
/// </summary>
public WindowPosition? MiniPosition
{
get; init;
}

/// <summary>Last saved monitor for <see cref="DisplayMode.Flex"/> mode. Only <see cref="WindowPosition.ScreenDeviceName"/> is used.</summary>
/// <summary>
/// Last saved monitor for <see cref="DisplayMode.Flex"/> mode. Only <see cref="WindowPosition.ScreenDeviceName"/> is used.
/// </summary>
public WindowPosition? FlexPosition
{
get; init;
}

/// <summary>Whether earnings should be deducted for a daily lunch break.</summary>
/// <summary>
/// Whether earnings should be deducted for a daily lunch break.
/// </summary>
public bool LunchBreakEnabled { get; init; } = false;

/// <summary>Lunch break start time; only used when <see cref="LunchBreakEnabled"/> is true.</summary>
/// <summary>
/// Lunch break start time; only used when <see cref="LunchBreakEnabled"/> is true.
/// </summary>
public TimeOnly LunchBreakStart { get; init; } = new(12, 0);

/// <summary>Lunch break end time; only used when <see cref="LunchBreakEnabled"/> is true.</summary>
/// <summary>
/// Lunch break end time; only used when <see cref="LunchBreakEnabled"/> is true.
/// </summary>
public TimeOnly LunchBreakEnd { get; init; } = new(13, 0);

/// <summary>Whether earnings accrue on Saturdays and Sundays. When false, weekends earn nothing.</summary>
/// <summary>
/// Whether earnings accrue on Saturdays and Sundays. When false, weekends earn nothing.
/// </summary>
public bool WorkOnWeekends { get; init; } = false;

/// <summary>Whether to show a tray balloon notification shortly before work ends.</summary>
/// <summary>
/// Whether to show a tray balloon notification shortly before work ends.
/// </summary>
public bool EnableEndOfDayReminder { get; init; } = false;

/// <summary>How many minutes before <see cref="WorkEnd"/> the end-of-day reminder fires.</summary>
/// <summary>
/// How many minutes before <see cref="WorkEnd"/> the end-of-day reminder fires.
/// </summary>
public int EndOfDayReminderMinutes { get; init; } = 5;

/// <summary>Whether to show a tray balloon notification each time earnings cross a <see cref="MilestoneAmount"/> increment.</summary>
/// <summary>
/// Whether to show a tray balloon notification each time earnings cross a <see cref="MilestoneAmount"/> increment.
/// </summary>
public bool EnableMilestoneNotifications { get; init; } = false;

/// <summary>Earnings increment that triggers a milestone notification (e.g. every ¥100).</summary>
/// <summary>
/// Earnings increment that triggers a milestone notification (e.g. every ¥100).
/// </summary>
public decimal MilestoneAmount { get; init; } = 100m;

/// <summary>
/// UI color theme. <c>"auto"</c> resolves from the Windows apps theme setting;
/// supported explicit values are <c>"light"</c> and <c>"dark"</c>.
/// </summary>
public string Theme { get; init; } = "auto";
}
4 changes: 4 additions & 0 deletions src/PayBeat.App/Resources/Strings.en.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
<sys:String x:Key="Settings.RunAtStartup">Run at Startup</sys:String>
<sys:String x:Key="Settings.Language">Language</sys:String>
<sys:String x:Key="Settings.LanguageAuto">Auto</sys:String>
<sys:String x:Key="Settings.Theme">Theme</sys:String>
<sys:String x:Key="Settings.ThemeAuto">Auto</sys:String>
<sys:String x:Key="Settings.ThemeLight">Light</sys:String>
<sys:String x:Key="Settings.ThemeDark">Dark</sys:String>
<sys:String x:Key="Settings.Hotkey">Toggle Visibility Hotkey</sys:String>
<sys:String x:Key="Settings.HotkeyHint">Click, then press a key combination</sys:String>
<sys:String x:Key="Settings.HotkeyCapturing">Press a key combination...</sys:String>
Expand Down
4 changes: 4 additions & 0 deletions src/PayBeat.App/Resources/Strings.zh-CN.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
<sys:String x:Key="Settings.RunAtStartup">开机自动启动</sys:String>
<sys:String x:Key="Settings.Language">语言</sys:String>
<sys:String x:Key="Settings.LanguageAuto">自动</sys:String>
<sys:String x:Key="Settings.Theme">主题</sys:String>
<sys:String x:Key="Settings.ThemeAuto">自动</sys:String>
<sys:String x:Key="Settings.ThemeLight">浅色</sys:String>
<sys:String x:Key="Settings.ThemeDark">深色</sys:String>
<sys:String x:Key="Settings.Hotkey">显示/隐藏快捷键</sys:String>
<sys:String x:Key="Settings.HotkeyHint">点击后输入快捷键组合</sys:String>
<sys:String x:Key="Settings.HotkeyCapturing">请输入快捷键组合...</sys:String>
Expand Down
49 changes: 14 additions & 35 deletions src/PayBeat.App/Resources/Styles.xaml
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<!-- Color palette: Catppuccin Mocha -->
<Color x:Key="BaseColor">#1E1E2E</Color>
<Color x:Key="SurfaceColor">#313244</Color>
<Color x:Key="OverlayColor">#45475A</Color>
<Color x:Key="TextColor">#CDD6F4</Color>
<Color x:Key="SubtextColor">#9399B2</Color>
<Color x:Key="MutedColor">#6C7086</Color>
<Color x:Key="GreenColor">#A6E3A1</Color>
<Color x:Key="BlueColor">#89B4FA</Color>
<Color x:Key="RedColor">#F38BA8</Color>

<SolidColorBrush x:Key="BaseBrush" Color="{StaticResource BaseColor}"/>
<SolidColorBrush x:Key="SurfaceBrush" Color="{StaticResource SurfaceColor}"/>
<SolidColorBrush x:Key="OverlayBrush" Color="{StaticResource OverlayColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>
<SolidColorBrush x:Key="SubtextBrush" Color="{StaticResource SubtextColor}"/>
<SolidColorBrush x:Key="MutedBrush" Color="{StaticResource MutedColor}"/>
<SolidColorBrush x:Key="GreenBrush" Color="{StaticResource GreenColor}"/>
<SolidColorBrush x:Key="BlueBrush" Color="{StaticResource BlueColor}"/>
<SolidColorBrush x:Key="RedBrush" Color="{StaticResource RedColor}"/>

<!-- Flat Button -->
<Style TargetType="Button">
<Setter Property="Cursor" Value="Hand"/>
Expand Down Expand Up @@ -56,7 +35,7 @@
<Style TargetType="TextBox">
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="CaretBrush" Value="{StaticResource BlueBrush}"/>
<Setter Property="CaretBrush" Value="{DynamicResource BlueBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
Expand All @@ -68,7 +47,7 @@
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource BlueBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource BlueBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Expand All @@ -78,9 +57,9 @@

<!-- ComboBox -->
<Style TargetType="ComboBox">
<Setter Property="Background" Value="#313244"/>
<Setter Property="Foreground" Value="#CDD6F4"/>
<Setter Property="BorderBrush" Value="#45475A"/>
<Setter Property="Background" Value="{DynamicResource SurfaceBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource OverlayBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="13"/>
Expand Down Expand Up @@ -108,7 +87,7 @@
IsHitTestVisible="False"/>
<Path Grid.Column="1"
Data="M 0 0 L 5 5 L 10 0"
Stroke="#9399B2"
Stroke="{DynamicResource SubtextBrush}"
StrokeThickness="1.5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Expand All @@ -130,8 +109,8 @@
Focusable="False"
PopupAnimation="Fade"
Placement="Bottom">
<Border Background="#313244"
BorderBrush="#45475A"
<Border Background="{DynamicResource SurfaceBrush}"
BorderBrush="{DynamicResource OverlayBrush}"
BorderThickness="1"
CornerRadius="6"
MinWidth="{TemplateBinding ActualWidth}"
Expand All @@ -149,10 +128,10 @@
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#89B4FA"/>
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource BlueBrush}"/>
</Trigger>
<Trigger Property="IsDropDownOpen" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#89B4FA"/>
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource BlueBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Expand All @@ -161,7 +140,7 @@
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="#CDD6F4"/>
<Setter Property="Foreground" Value="{DynamicResource TextBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="10,7"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
Expand All @@ -177,11 +156,11 @@
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bg" Property="Background" Value="#45475A"/>
<Setter TargetName="Bg" Property="Background" Value="{DynamicResource OverlayBrush}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Bg" Property="Background" Value="#45475A"/>
<Setter Property="Foreground" Value="#89B4FA"/>
<Setter TargetName="Bg" Property="Background" Value="{DynamicResource OverlayBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource BlueBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Expand Down
28 changes: 28 additions & 0 deletions src/PayBeat.App/Resources/Theme.Dark.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<!-- Color palette: Catppuccin Mocha (dark) -->
<Color x:Key="BaseColor">#1E1E2E</Color>
<Color x:Key="SurfaceColor">#313244</Color>
<Color x:Key="OverlayColor">#45475A</Color>
<Color x:Key="TextColor">#CDD6F4</Color>
<Color x:Key="SubtextColor">#9399B2</Color>
<Color x:Key="MutedColor">#6C7086</Color>
<Color x:Key="GreenColor">#A6E3A1</Color>
<Color x:Key="BlueColor">#89B4FA</Color>
<Color x:Key="RedColor">#F38BA8</Color>
<Color x:Key="CaptureBackgroundColor">#1C3A5E</Color>
<Color x:Key="FlexGradientMidColor">#181825</Color>

<SolidColorBrush x:Key="BaseBrush" Color="{StaticResource BaseColor}"/>
<SolidColorBrush x:Key="SurfaceBrush" Color="{StaticResource SurfaceColor}"/>
<SolidColorBrush x:Key="OverlayBrush" Color="{StaticResource OverlayColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>
<SolidColorBrush x:Key="SubtextBrush" Color="{StaticResource SubtextColor}"/>
<SolidColorBrush x:Key="MutedBrush" Color="{StaticResource MutedColor}"/>
<SolidColorBrush x:Key="GreenBrush" Color="{StaticResource GreenColor}"/>
<SolidColorBrush x:Key="BlueBrush" Color="{StaticResource BlueColor}"/>
<SolidColorBrush x:Key="RedBrush" Color="{StaticResource RedColor}"/>
<SolidColorBrush x:Key="CaptureBackgroundBrush" Color="{StaticResource CaptureBackgroundColor}"/>

</ResourceDictionary>
28 changes: 28 additions & 0 deletions src/PayBeat.App/Resources/Theme.Light.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<!-- Color palette: Catppuccin Latte (light) -->
<Color x:Key="BaseColor">#EFF1F5</Color>
<Color x:Key="SurfaceColor">#E6E9EF</Color>
<Color x:Key="OverlayColor">#CCD0DA</Color>
<Color x:Key="TextColor">#4C4F69</Color>
<Color x:Key="SubtextColor">#6C6F85</Color>
<Color x:Key="MutedColor">#8C8FA1</Color>
<Color x:Key="GreenColor">#40A02B</Color>
<Color x:Key="BlueColor">#1E66F5</Color>
<Color x:Key="RedColor">#D20F39</Color>
<Color x:Key="CaptureBackgroundColor">#DCE8FF</Color>
<Color x:Key="FlexGradientMidColor">#DCE0E8</Color>

<SolidColorBrush x:Key="BaseBrush" Color="{StaticResource BaseColor}"/>
<SolidColorBrush x:Key="SurfaceBrush" Color="{StaticResource SurfaceColor}"/>
<SolidColorBrush x:Key="OverlayBrush" Color="{StaticResource OverlayColor}"/>
<SolidColorBrush x:Key="TextBrush" Color="{StaticResource TextColor}"/>
<SolidColorBrush x:Key="SubtextBrush" Color="{StaticResource SubtextColor}"/>
<SolidColorBrush x:Key="MutedBrush" Color="{StaticResource MutedColor}"/>
<SolidColorBrush x:Key="GreenBrush" Color="{StaticResource GreenColor}"/>
<SolidColorBrush x:Key="BlueBrush" Color="{StaticResource BlueColor}"/>
<SolidColorBrush x:Key="RedBrush" Color="{StaticResource RedColor}"/>
<SolidColorBrush x:Key="CaptureBackgroundBrush" Color="{StaticResource CaptureBackgroundColor}"/>

</ResourceDictionary>
Loading
Loading