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
8 changes: 7 additions & 1 deletion CopyPaste.Core/MyMConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,13 @@ public sealed class MyMConfig
/// ID of the active theme (e.g., "copypaste.default").
/// Must match <see cref="CopyPaste.Core.Themes.ITheme.Id"/>.
/// </summary>
public string ThemeId { get; set; } = "copypaste.default";
public string ThemeId { get; set; } = "copypaste.compact";

/// <summary>
/// Whether the user has already seen (and dismissed) the theme discovery hint in Settings.
/// Once true, the hint banner is never shown again.
/// </summary>
public bool HasSeenThemeHint { get; set; }

// ═══════════════════════════════════════════════════════════════
// Hotkey Configuration
Expand Down
12 changes: 12 additions & 0 deletions CopyPaste.UI/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
InitializeCoreServices();

var config = _engine!.Config;
MigrateFirstRunToCompact(config);
L.Initialize(new LocalizationService(config.PreferredLanguage));

_themeRegistry = new ThemeRegistry();
Expand All @@ -113,6 +114,17 @@
AppLogger.Info("Main window launched");
}

// Ensures fresh installs and users who never changed their theme get Compact by default.
// Only runs once (guarded by HasSeenThemeHint = false on first run).
private static void MigrateFirstRunToCompact(MyMConfig config)
{
if (!config.HasSeenThemeHint && config.ThemeId == "copypaste.default")
{
config.ThemeId = "copypaste.compact";
ConfigLoader.Save(config);
}
}

private void InitializeCoreServices()
{
_engine = new CopyPasteEngine(svc => new WindowsClipboardListener(svc));
Expand Down Expand Up @@ -209,7 +221,7 @@
const uint MB_OK = 0x00000000;
const uint MB_ICONINFORMATION = 0x00000040;

_ = MessageBox(hwnd, message, caption, MB_OK | MB_ICONINFORMATION);

Check warning on line 224 in CopyPaste.UI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Remove this unnecessary bit operation. (https://rules.sonarsource.com/csharp/RSPEC-2437)

Check warning on line 224 in CopyPaste.UI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Remove this unnecessary bit operation. (https://rules.sonarsource.com/csharp/RSPEC-2437)
}
catch (Exception ex)
{
Expand All @@ -229,7 +241,7 @@
AppLogger.Info("Signaled launcher that app is ready");
}
catch (WaitHandleCannotBeOpenedException)
{

Check warning on line 244 in CopyPaste.UI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Either remove or fill this block of code. (https://rules.sonarsource.com/csharp/RSPEC-108)
}
catch (Exception ex)
{
Expand All @@ -237,7 +249,7 @@
}
}

private void OnUnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)

Check warning on line 252 in CopyPaste.UI/App.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Make 'OnUnhandledException' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)
{
AppLogger.Exception(e.Exception, "Unhandled exception");
e.Handled = true;
Expand Down
6 changes: 5 additions & 1 deletion CopyPaste.UI/Localization/Languages/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
"selectColors": "Select colors...",
"selectTypes": "Select types..."
},
"emptyState": "No items in this section"
"emptyState": "No items in this section",
"firstRun": {
"text": "Customize your experience in",
"action": "Settings"
}
},
"config": {
"window": {
Expand Down
6 changes: 5 additions & 1 deletion CopyPaste.UI/Localization/Languages/es-CL.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
"selectColors": "Seleccionar colores...",
"selectTypes": "Seleccionar tipos..."
},
"emptyState": "No hay elementos en esta sección"
"emptyState": "No hay elementos en esta sección",
"firstRun": {
"text": "Personaliza tu experiencia en",
"action": "Ajustes"
}
},
"config": {
"window": {
Expand Down
50 changes: 39 additions & 11 deletions CopyPaste.UI/Themes/Compact/CompactWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
Expand Down Expand Up @@ -91,7 +92,7 @@
</Grid>

<!-- Search/Filter Bar -->
<Grid Grid.Row="2" Margin="12,4,12,6">
<Grid Grid.Row="3" Margin="12,4,12,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
Expand Down Expand Up @@ -272,8 +273,32 @@
</Button>
</Grid>

<!-- First-run tip (shown once) -->
<Grid x:Name="FirstRunBanner"
Grid.Row="1"
Visibility="Collapsed"
Margin="14,2,10,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Spacing="3" VerticalAlignment="Center">
<TextBlock x:Name="FirstRunText" FontSize="11" Opacity="0.45"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"/>
<HyperlinkButton x:Name="FirstRunAction" Padding="2,0" FontSize="11"
Opacity="0.6" Click="FirstRunAction_Click"
VerticalAlignment="Center"/>
</StackPanel>
<Button Grid.Column="1" Width="18" Height="18" Padding="0"
Background="Transparent" BorderThickness="0" CornerRadius="3"
Click="FirstRunBanner_Close" VerticalAlignment="Center">
<FontIcon Glyph="&#xE711;" FontSize="7" Opacity="0.3"/>
</Button>
</Grid>

<!-- Empty State -->
<StackPanel Grid.Row="3"
<StackPanel Grid.Row="4"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8"
Expand All @@ -287,7 +312,7 @@

<!-- Clipboard Items List -->
<ListView x:Name="ClipboardListView"
Grid.Row="3"
Grid.Row="4"
ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}"
SelectionMode="Single"
IsTabStop="True"
Expand Down Expand Up @@ -372,13 +397,15 @@
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="5">
<FontIcon Glyph="{x:Bind TypeIcon}" FontSize="10" Opacity="0.4"/>
<StackPanel Grid.Column="0" Orientation="Horizontal" Spacing="5" VerticalAlignment="Center">
<FontIcon Glyph="{x:Bind TypeIcon}" FontSize="10" Opacity="0.4" VerticalAlignment="Center"/>
<TextBlock Text="{x:Bind Label, Mode=OneWay}" FontSize="11" Opacity="0.5"
FontWeight="SemiBold" MaxLines="1" TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"
Visibility="{x:Bind LabelVisibility, Mode=OneWay}"/>
<TextBlock Text="{x:Bind HeaderTitle}" FontSize="11" Opacity="0.4"
MaxLines="1" Visibility="{x:Bind DefaultHeaderVisibility, Mode=OneWay}"/>
MaxLines="1" VerticalAlignment="Center"
Visibility="{x:Bind DefaultHeaderVisibility, Mode=OneWay}"/>
</StackPanel>
<Grid Grid.Column="1" VerticalAlignment="Center">
<!-- Timestamp (visible by default, hidden on hover) -->
Expand Down Expand Up @@ -443,17 +470,18 @@
<!-- File warning -->
<StackPanel Grid.Row="2" Orientation="Horizontal" Margin="0,4,0,0"
Visibility="{x:Bind FileWarningVisibility, Mode=OneWay}">
<FontIcon Glyph="&#xE7BA;" FontSize="10" Foreground="#FFAA00" Margin="0,0,4,0"/>
<TextBlock Text="{x:Bind FileWarningText}" FontSize="10" Foreground="#FFAA00"/>
<FontIcon Glyph="&#xE7BA;" FontSize="10" Foreground="#FFAA00" Margin="0,0,4,0" VerticalAlignment="Center"/>
<TextBlock Text="{x:Bind FileWarningText}" FontSize="10" Foreground="#FFAA00" VerticalAlignment="Center"/>
</StackPanel>

<!-- Card footer: source + size + paste count -->
<Grid Grid.Row="3" Margin="0,6,0,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Spacing="4"
Visibility="{x:Bind AppSourceVisibility}">
<FontIcon Glyph="&#xE7C3;" FontSize="9" Opacity="0.3"/>
<FontIcon Glyph="&#xE7C3;" FontSize="9" Opacity="0.3" VerticalAlignment="Center"/>
<TextBlock Text="{x:Bind AppSource}" FontSize="10" Opacity="0.35"
MaxLines="1" TextTrimming="CharacterEllipsis" MaxWidth="140"/>
MaxLines="1" TextTrimming="CharacterEllipsis" MaxWidth="140"
VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="6">
<TextBlock Text="{x:Bind PasteCountDisplay, Mode=OneWay}" FontSize="10" Opacity="0.35"
Expand All @@ -474,7 +502,7 @@
</ListView>

<!-- Bottom Branding Bar -->
<Grid Grid.Row="4" Margin="12,4,12,8">
<Grid Grid.Row="5" Margin="12,4,12,8">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
Spacing="6"
Expand Down
27 changes: 27 additions & 0 deletions CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
internal CompactViewModel ViewModel { get; }

[StructLayout(LayoutKind.Sequential)]
private struct POINT

Check warning on line 34 in CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Rename struct 'POINT' to match pascal case naming rules, consider using 'Point'. (https://rules.sonarsource.com/csharp/RSPEC-101)

Check warning on line 34 in CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Rename struct 'POINT' to match pascal case naming rules, consider using 'Point'. (https://rules.sonarsource.com/csharp/RSPEC-101)
{
public int X;
public int Y;
Expand Down Expand Up @@ -143,6 +143,12 @@
FilterModeCategory.Text = L.Get("ui.filterMode.category", "Category");
FilterModeType.Text = L.Get("ui.filterMode.type", "Type");

FirstRunText.Text = L.Get("ui.firstRun.text", "Personaliza tu experiencia en");
FirstRunAction.Content = L.Get("ui.firstRun.action", "Ajustes");

if (!_config.HasSeenThemeHint)
FirstRunBanner.Visibility = Microsoft.UI.Xaml.Visibility.Visible;

ColorLabelRed.Text = L.Get("clipboard.editDialog.colorRed", "Red");
ColorLabelGreen.Text = L.Get("clipboard.editDialog.colorGreen", "Green");
ColorLabelPurple.Text = L.Get("clipboard.editDialog.colorPurple", "Purple");
Expand Down Expand Up @@ -258,6 +264,27 @@
}
}

private void FirstRunBanner_Close(object sender, RoutedEventArgs e)
{
FirstRunBanner.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
DismissFirstRunHint();
}

private void FirstRunAction_Click(object sender, RoutedEventArgs e)
{
FirstRunBanner.Visibility = Microsoft.UI.Xaml.Visibility.Collapsed;
DismissFirstRunHint();
_context.OpenSettings();
}

private static void DismissFirstRunHint()
{
var cfg = ConfigLoader.Load();
if (cfg.HasSeenThemeHint) return;
cfg.HasSeenThemeHint = true;
ConfigLoader.Save(cfg);
}

private void AppWindow_Changed(AppWindow sender, AppWindowChangedEventArgs args)
{
if ((args.DidPresenterChange || args.DidVisibilityChange) && !sender.IsVisible)
Expand All @@ -278,7 +305,7 @@
private void TrayMenuSettings_Click(object sender, RoutedEventArgs e) =>
_context.OpenSettings();

private void Card_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)

Check warning on line 308 in CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Make 'Card_PointerEntered' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)
{
if (sender is not Border border) return;
var timestamp = ClipboardWindowHelpers.FindChild<TextBlock>(border, "TimestampText");
Expand All @@ -287,7 +314,7 @@
if (actions != null) actions.Opacity = 1;
}

private void Card_PointerExited(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)

Check warning on line 317 in CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Make 'Card_PointerExited' a static method. (https://rules.sonarsource.com/csharp/RSPEC-2325)
{
if (sender is not Border border) return;
var timestamp = ClipboardWindowHelpers.FindChild<TextBlock>(border, "TimestampText");
Expand Down Expand Up @@ -452,7 +479,7 @@
{
if (e.Key is Windows.System.VirtualKey.Enter or Windows.System.VirtualKey.Down)
{
if (ClipboardListView.Items.Count > 0)

Check warning on line 482 in CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Merge this if statement with the enclosing one. (https://rules.sonarsource.com/csharp/RSPEC-1066)
{
ClipboardListView.SelectedIndex = 0;

Expand All @@ -470,7 +497,7 @@
}
}

private void RootGrid_PreviewKeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e)

Check warning on line 500 in CopyPaste.UI/Themes/Compact/CompactWindow.xaml.cs

View workflow job for this annotation

GitHub Actions / SonarCloud

Refactor this method to reduce its Cognitive Complexity from 17 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)
{
// ESC clears current filter
if (e.Key == Windows.System.VirtualKey.Escape)
Expand Down
2 changes: 1 addition & 1 deletion CopyPaste.UI/Themes/Default/DefaultTheme.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace CopyPaste.UI.Themes;
internal sealed class DefaultTheme : ITheme
{
public string Id => "copypaste.default";
public string Name => "Default";
public string Name => "Full";
public string Version => "1.0.0";
public string Author => "CopyPaste";

Expand Down
4 changes: 2 additions & 2 deletions Tests/CopyPaste.Core.Tests/MyMConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ public void DefaultConfig_RunOnStartup_IsTrue()
}

[Fact]
public void DefaultConfig_ThemeId_IsDefault()
public void DefaultConfig_ThemeId_IsCompact()
{
var config = new MyMConfig();
Assert.Equal("copypaste.default", config.ThemeId);
Assert.Equal("copypaste.compact", config.ThemeId);
}

[Fact]
Expand Down
Loading
Loading