From 3be23af16c3dd4874b30b657300ffe0dcfc50bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Sun, 11 Jan 2026 13:28:57 +0100 Subject: [PATCH 01/14] Redesign modal dialogs with design system and utility classes - Add reusable design tokens (colors, spacing, typography) in DesignSystem.axaml - Create Tailwind-like utility classes in Utilities.axaml - Redesign modal dialogs with modern styling - Simplify view styles across all major views - Apply consistent design patterns throughout UI Co-Authored-By: Claude Opus 4.5 --- src/RemoteViewer.Client/App.axaml | 62 +- .../Controls/DialogHeader.axaml | 29 + .../Controls/DialogHeader.axaml.cs | 68 ++ .../FileTransferConfirmationDialog.axaml | 84 +- .../Dialogs/ViewerSelectionDialog.axaml | 117 +-- .../Controls/DropOverlay.axaml | 22 +- .../Controls/IconBadge.axaml | 19 + .../Controls/IconBadge.axaml.cs | 88 +++ .../Controls/Toasts/ToastsView.axaml | 10 +- .../Themes/BadgeStyles.axaml | 150 ++++ src/RemoteViewer.Client/Themes/Borders.axaml | 31 + .../Themes/ButtonStyles.axaml | 223 ++++++ .../Themes/CardStyles.axaml | 126 +++ src/RemoteViewer.Client/Themes/Colors.axaml | 155 ++++ src/RemoteViewer.Client/Themes/Spacing.axaml | 58 ++ .../Themes/Typography.axaml | 128 +++ .../Themes/Utilities.axaml | 745 ++++++++++++++++++ .../Views/About/AboutView.axaml | 115 +-- .../Views/Chat/ChatView.axaml | 57 +- .../Views/Main/MainView.axaml | 114 +-- .../Views/Presenter/PresenterView.axaml | 141 ++-- .../Views/Viewer/ViewerView.axaml | 101 +-- 22 files changed, 2145 insertions(+), 498 deletions(-) create mode 100644 src/RemoteViewer.Client/Controls/DialogHeader.axaml create mode 100644 src/RemoteViewer.Client/Controls/DialogHeader.axaml.cs create mode 100644 src/RemoteViewer.Client/Controls/IconBadge.axaml create mode 100644 src/RemoteViewer.Client/Controls/IconBadge.axaml.cs create mode 100644 src/RemoteViewer.Client/Themes/BadgeStyles.axaml create mode 100644 src/RemoteViewer.Client/Themes/Borders.axaml create mode 100644 src/RemoteViewer.Client/Themes/ButtonStyles.axaml create mode 100644 src/RemoteViewer.Client/Themes/CardStyles.axaml create mode 100644 src/RemoteViewer.Client/Themes/Colors.axaml create mode 100644 src/RemoteViewer.Client/Themes/Spacing.axaml create mode 100644 src/RemoteViewer.Client/Themes/Typography.axaml create mode 100644 src/RemoteViewer.Client/Themes/Utilities.axaml diff --git a/src/RemoteViewer.Client/App.axaml b/src/RemoteViewer.Client/App.axaml index 598bfcb..3fac010 100644 --- a/src/RemoteViewer.Client/App.axaml +++ b/src/RemoteViewer.Client/App.axaml @@ -8,52 +8,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + @@ -64,5 +24,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml b/src/RemoteViewer.Client/Controls/DialogHeader.axaml new file mode 100644 index 0000000..9d76229 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml.cs b/src/RemoteViewer.Client/Controls/DialogHeader.axaml.cs new file mode 100644 index 0000000..b4dc02e --- /dev/null +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml.cs @@ -0,0 +1,68 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Material.Icons; + +namespace RemoteViewer.Client.Controls; + +public partial class DialogHeader : UserControl +{ + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon), MaterialIconKind.Information); + + public static readonly StyledProperty IconSizeProperty = + AvaloniaProperty.Register(nameof(IconSize), IconBadgeSize.Medium); + + public static readonly StyledProperty IconForegroundProperty = + AvaloniaProperty.Register(nameof(IconForeground)); + + public static readonly StyledProperty IconBackgroundProperty = + AvaloniaProperty.Register(nameof(IconBackground)); + + public static readonly StyledProperty TitleProperty = + AvaloniaProperty.Register(nameof(Title)); + + public static readonly StyledProperty SubtitleProperty = + AvaloniaProperty.Register(nameof(Subtitle)); + + public MaterialIconKind Icon + { + get => this.GetValue(IconProperty); + set => this.SetValue(IconProperty, value); + } + + public IconBadgeSize IconSize + { + get => this.GetValue(IconSizeProperty); + set => this.SetValue(IconSizeProperty, value); + } + + public IBrush? IconForeground + { + get => this.GetValue(IconForegroundProperty); + set => this.SetValue(IconForegroundProperty, value); + } + + public IBrush? IconBackground + { + get => this.GetValue(IconBackgroundProperty); + set => this.SetValue(IconBackgroundProperty, value); + } + + public string? Title + { + get => this.GetValue(TitleProperty); + set => this.SetValue(TitleProperty, value); + } + + public string? Subtitle + { + get => this.GetValue(SubtitleProperty); + set => this.SetValue(SubtitleProperty, value); + } + + public DialogHeader() + { + this.InitializeComponent(); + } +} diff --git a/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml index ca1908c..39a1d7c 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml @@ -1,80 +1,68 @@ - + - - - - - - + - - - + + - - + + + + + + Classes="header-small" + TextTrimming="CharacterEllipsis" + ToolTip.Tip="{Binding FileName}"/> + Classes="small"/> - - + + - - + + + diff --git a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml index a12f9d6..89fc56e 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml @@ -1,6 +1,7 @@ - + MaxWidth="400"> - - - - - - + - - - + + - - + + + + + + Classes="header-small truncate" + ToolTip.Tip="{Binding FileName}"/> + Classes="small"/> - - + + - - - - - - - - + + + - - + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Controls/DropOverlay.axaml b/src/RemoteViewer.Client/Controls/DropOverlay.axaml index 571fa0f..1f2b3df 100644 --- a/src/RemoteViewer.Client/Controls/DropOverlay.axaml +++ b/src/RemoteViewer.Client/Controls/DropOverlay.axaml @@ -5,37 +5,33 @@ x:Name="Root"> + Padding="{StaticResource SpacingLG}"> - + + Classes="overlay-card"> - + - + diff --git a/src/RemoteViewer.Client/Controls/IconBadge.axaml b/src/RemoteViewer.Client/Controls/IconBadge.axaml new file mode 100644 index 0000000..2b71562 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/IconBadge.axaml @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/RemoteViewer.Client/Controls/IconBadge.axaml.cs b/src/RemoteViewer.Client/Controls/IconBadge.axaml.cs new file mode 100644 index 0000000..a000a85 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/IconBadge.axaml.cs @@ -0,0 +1,88 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Material.Icons; + +namespace RemoteViewer.Client.Controls; + +public enum IconBadgeSize +{ + Small, + Medium, + Large +} + +public partial class IconBadge : UserControl +{ + public static readonly StyledProperty IconProperty = + AvaloniaProperty.Register(nameof(Icon), MaterialIconKind.Star); + + public static readonly StyledProperty SizeProperty = + AvaloniaProperty.Register(nameof(Size), IconBadgeSize.Medium); + + public static readonly StyledProperty IconForegroundProperty = + AvaloniaProperty.Register(nameof(IconForeground)); + + public static readonly StyledProperty BadgeBackgroundProperty = + AvaloniaProperty.Register(nameof(BadgeBackground)); + + public MaterialIconKind Icon + { + get => this.GetValue(IconProperty); + set => this.SetValue(IconProperty, value); + } + + public IconBadgeSize Size + { + get => this.GetValue(SizeProperty); + set => this.SetValue(SizeProperty, value); + } + + public IBrush? IconForeground + { + get => this.GetValue(IconForegroundProperty); + set => this.SetValue(IconForegroundProperty, value); + } + + public IBrush? BadgeBackground + { + get => this.GetValue(BadgeBackgroundProperty); + set => this.SetValue(BadgeBackgroundProperty, value); + } + + public IconBadge() + { + this.InitializeComponent(); + this.UpdateSize(); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == SizeProperty) + { + this.UpdateSize(); + } + } + + private void UpdateSize() + { + if (this.BadgeBorder == null || this.BadgeIcon == null) + return; + + var (badgeSize, cornerRadius, iconSize) = this.Size switch + { + IconBadgeSize.Small => (32.0, 8.0, 16.0), + IconBadgeSize.Medium => (40.0, 10.0, 22.0), + IconBadgeSize.Large => (64.0, 32.0, 32.0), + _ => (40.0, 10.0, 22.0) + }; + + this.BadgeBorder.Width = badgeSize; + this.BadgeBorder.Height = badgeSize; + this.BadgeBorder.CornerRadius = new CornerRadius(cornerRadius); + this.BadgeIcon.Width = iconSize; + this.BadgeIcon.Height = iconSize; + } +} diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index 95d3a60..1ad4a11 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -11,7 +11,7 @@ - + @@ -184,7 +184,7 @@ - + - + + Foreground="{DynamicResource OnAccentTextBrush}"/> + Foreground="{DynamicResource OnAccentTextBrush}"/> diff --git a/src/RemoteViewer.Client/Themes/BadgeStyles.axaml b/src/RemoteViewer.Client/Themes/BadgeStyles.axaml new file mode 100644 index 0000000..a8906b1 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/BadgeStyles.axaml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Themes/Borders.axaml b/src/RemoteViewer.Client/Themes/Borders.axaml new file mode 100644 index 0000000..ce108c6 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/Borders.axaml @@ -0,0 +1,31 @@ + + + + + + + + 0 + 4 + 8 + 12 + 20 + 32 + + + 6 + 8 + 12 + + + + + + 12 + 16 + 20 + 24 + 32 + + diff --git a/src/RemoteViewer.Client/Themes/ButtonStyles.axaml b/src/RemoteViewer.Client/Themes/ButtonStyles.axaml new file mode 100644 index 0000000..d5cf2ea --- /dev/null +++ b/src/RemoteViewer.Client/Themes/ButtonStyles.axaml @@ -0,0 +1,223 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Themes/CardStyles.axaml b/src/RemoteViewer.Client/Themes/CardStyles.axaml new file mode 100644 index 0000000..2b767a5 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/CardStyles.axaml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Themes/Colors.axaml b/src/RemoteViewer.Client/Themes/Colors.axaml new file mode 100644 index 0000000..c1970d6 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/Colors.axaml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Themes/Spacing.axaml b/src/RemoteViewer.Client/Themes/Spacing.axaml new file mode 100644 index 0000000..42e4f45 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/Spacing.axaml @@ -0,0 +1,58 @@ + + + + + + + + + 4 + 8 + 12 + 16 + 24 + 32 + + + 4,0 + 8,0 + 12,0 + 16,0 + 24,0 + + + 0,4 + 0,8 + 0,12 + 0,16 + 0,24 + + + 20 + 24 + 16 + + + 14 + 16 + + + + + + + 2 + 4 + 8 + 12 + 16 + 24 + 32 + + + 6 + 10 + 16 + + diff --git a/src/RemoteViewer.Client/Themes/Typography.axaml b/src/RemoteViewer.Client/Themes/Typography.axaml new file mode 100644 index 0000000..e8c14d0 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/Typography.axaml @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Themes/Utilities.axaml b/src/RemoteViewer.Client/Themes/Utilities.axaml new file mode 100644 index 0000000..353aeb5 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/Utilities.axaml @@ -0,0 +1,745 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Views/About/AboutView.axaml b/src/RemoteViewer.Client/Views/About/AboutView.axaml index 0182f76..3d0adce 100644 --- a/src/RemoteViewer.Client/Views/About/AboutView.axaml +++ b/src/RemoteViewer.Client/Views/About/AboutView.axaml @@ -1,5 +1,6 @@ - + + - - + + + - + + - + - + + + Classes="card-lg"> - + + + + - + + Classes="header-small"/> - + + + + Classes="flex-row gap-lg mt-sm"> + Classes="p-0 cursor-pointer"> + + + + + + Classes="p-0 cursor-pointer"> + + + + + @@ -94,10 +101,12 @@ diff --git a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml index 405ad87..ec8b1dc 100644 --- a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml +++ b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml @@ -1,6 +1,7 @@ - + - + Classes="wrap" + FontSize="13" + LineHeight="20"/> + Classes="mt-xs"> diff --git a/src/RemoteViewer.Client/Views/Main/MainView.axaml b/src/RemoteViewer.Client/Views/Main/MainView.axaml index af6ed40..aa6a641 100644 --- a/src/RemoteViewer.Client/Views/Main/MainView.axaml +++ b/src/RemoteViewer.Client/Views/Main/MainView.axaml @@ -1,5 +1,6 @@ + Classes="bg-elevated px-md py-sm"> - + + Width="{StaticResource IconSizeSM}" Height="{StaticResource IconSizeSM}" + Foreground="{Binding IsConnected, Converter={conv:BoolToObject TrueValue={StaticResource SuccessBrush}, FalseValue={StaticResource ErrorBrush}}}"/> + Classes="caption justify-center"/> - + + Classes="card-lg mr-sm"> + Classes="header-small mb-md"/> - + + Classes="caption secondary"/> + Classes="credential my-sm"/> - + - + Classes="caption secondary"/> + + Classes="credential my-sm justify-center"/> @@ -118,19 +93,14 @@ - + Classes="card-lg ml-sm"> + + Classes="header-small"/> - + + Classes="caption secondary"/> - + + Classes="caption secondary"/> @@ -167,26 +134,21 @@ + Classes="items-end justify-end mr-sm mb-sm"/> - + Classes="justify-center mr-md"/> + Classes="justify-center"/> diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index b0c692b..5c36155 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -24,16 +24,12 @@ - + Classes="bg-elevated px-sm py-sm"> + - - + - + + Classes="header-small mb-md"/> - + + Classes="caption secondary"/> + Classes="credential my-sm"/> - + - + Classes="caption secondary"/> + + Classes="credential my-sm justify-center"/> @@ -205,45 +164,37 @@ - + Classes="header-small mb-md"/> - + - - + + + Classes="justify-center"/> @@ -75,16 +71,13 @@ - + - + @@ -242,12 +211,10 @@ + Classes="items-start justify-center mx-lg"> - + @@ -273,12 +240,10 @@ + Classes="items-end justify-center mx-lg"> - + @@ -304,12 +269,10 @@ + Classes="items-center justify-start my-lg"> - + @@ -335,12 +298,10 @@ + Classes="items-center justify-end my-lg"> - + @@ -369,9 +330,7 @@ + Classes="items-end justify-end mr-sm mb-sm"/> Date: Sun, 11 Jan 2026 15:25:14 +0100 Subject: [PATCH 02/14] Replace utility classes with markup extensions and Card component - Add SpacingExtension for composable margins/padding: {theme:Spacing Top=SM, X=MD} - Add GapExtension for StackPanel spacing: {theme:Gap LG} - Add GridSpacingExtension for grid spacer columns: {theme:GridSpacing SM} - Add Card component with Variant and Accent properties - Refactor IconBadge to use XAML styles instead of code-behind - Remove Utilities.axaml (745 lines) and Spacing.axaml - Migrate all views and dialogs to use new design system Co-Authored-By: Claude Opus 4.5 --- src/RemoteViewer.Client/App.axaml | 4 - src/RemoteViewer.Client/Controls/Card.axaml | 66 ++ .../Controls/Card.axaml.cs | 48 ++ .../Controls/DialogHeader.axaml | 5 +- .../FileTransferConfirmationDialog.axaml | 37 +- .../Dialogs/ViewerSelectionDialog.axaml | 59 +- .../Controls/DropOverlay.axaml | 7 +- .../Controls/IconBadge.axaml | 35 + .../Controls/IconBadge.axaml.cs | 31 - .../Controls/Toasts/ToastsView.axaml | 6 +- .../Themes/GapExtension.cs | 27 + .../Themes/GridSpacingExtension.cs | 16 + src/RemoteViewer.Client/Themes/Spacing.axaml | 58 -- .../Themes/SpacingExtension.cs | 41 + .../Themes/Utilities.axaml | 745 ------------------ .../Views/About/AboutView.axaml | 70 +- .../Views/Chat/ChatView.axaml | 39 +- .../Views/Main/MainView.axaml | 125 +-- .../Views/Presenter/PresenterView.axaml | 84 +- .../Views/Viewer/ViewerView.axaml | 65 +- 20 files changed, 545 insertions(+), 1023 deletions(-) create mode 100644 src/RemoteViewer.Client/Controls/Card.axaml create mode 100644 src/RemoteViewer.Client/Controls/Card.axaml.cs create mode 100644 src/RemoteViewer.Client/Themes/GapExtension.cs create mode 100644 src/RemoteViewer.Client/Themes/GridSpacingExtension.cs delete mode 100644 src/RemoteViewer.Client/Themes/Spacing.axaml create mode 100644 src/RemoteViewer.Client/Themes/SpacingExtension.cs delete mode 100644 src/RemoteViewer.Client/Themes/Utilities.axaml diff --git a/src/RemoteViewer.Client/App.axaml b/src/RemoteViewer.Client/App.axaml index 3fac010..45abba4 100644 --- a/src/RemoteViewer.Client/App.axaml +++ b/src/RemoteViewer.Client/App.axaml @@ -11,7 +11,6 @@ - @@ -31,8 +30,5 @@ - - - \ No newline at end of file diff --git a/src/RemoteViewer.Client/Controls/Card.axaml b/src/RemoteViewer.Client/Controls/Card.axaml new file mode 100644 index 0000000..6294bc9 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/Card.axaml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Controls/Card.axaml.cs b/src/RemoteViewer.Client/Controls/Card.axaml.cs new file mode 100644 index 0000000..6b9ef50 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/Card.axaml.cs @@ -0,0 +1,48 @@ +using Avalonia; +using Avalonia.Controls; + +namespace RemoteViewer.Client.Controls; + +public enum CardVariant +{ + Default, + Elevated, + Surface, + AccentStrip +} + +public enum CardAccent +{ + None, + Success, + Error, + Info, + Warning, + Primary +} + +public partial class Card : ContentControl +{ + public static readonly StyledProperty VariantProperty = + AvaloniaProperty.Register(nameof(Variant), CardVariant.Default); + + public static readonly StyledProperty AccentProperty = + AvaloniaProperty.Register(nameof(Accent), CardAccent.None); + + public CardVariant Variant + { + get => this.GetValue(VariantProperty); + set => this.SetValue(VariantProperty, value); + } + + public CardAccent Accent + { + get => this.GetValue(AccentProperty); + set => this.SetValue(AccentProperty, value); + } + + public Card() + { + this.InitializeComponent(); + } +} diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml b/src/RemoteViewer.Client/Controls/DialogHeader.axaml index 9d76229..bf33280 100644 --- a/src/RemoteViewer.Client/Controls/DialogHeader.axaml +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml @@ -1,6 +1,7 @@ @@ -12,13 +13,13 @@ Size="{Binding IconSize, ElementName=Root}" IconForeground="{Binding IconForeground, ElementName=Root}" BadgeBackground="{Binding IconBackground, ElementName=Root}" - Margin="{StaticResource SpacingHorizontalMD}" + Margin="{theme:Spacing Right=MD}" VerticalAlignment="Center"/> + Spacing="{theme:Gap XS}"> - + - + - + - + Margin="{theme:Spacing Right=MD}" + VerticalAlignment="Center" + Foreground="{DynamicResource TextSecondaryBrush}"/> + - + - + + + + + + + diff --git a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml index 89fc56e..01c18ba 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml @@ -4,6 +4,7 @@ xmlns:controls="using:RemoteViewer.Client.Controls" xmlns:dialogs="using:RemoteViewer.Client.Controls.Dialogs" xmlns:presenter="using:RemoteViewer.Client.Views.Presenter" + xmlns:theme="using:RemoteViewer.Client.Themes" x:Class="RemoteViewer.Client.Controls.Dialogs.ViewerSelectionDialog" x:DataType="dialogs:ViewerSelectionDialogViewModel" Title="Send File" @@ -13,11 +14,11 @@ ShowInTaskbar="False" WindowStartupLocation="CenterOwner"> - - + - - + + - + Margin="{theme:Spacing X=MD}" + VerticalAlignment="Center" + Foreground="{DynamicResource TextSecondaryBrush}"/> + - + - + - + - + Cursor="Hand"> + + VerticalAlignment="Center" + Foreground="{DynamicResource TextSecondaryBrush}"/> + Classes="body-small" + VerticalAlignment="Center"/> @@ -72,22 +79,30 @@ - + + + + + + + diff --git a/src/RemoteViewer.Client/Controls/DropOverlay.axaml b/src/RemoteViewer.Client/Controls/DropOverlay.axaml index 1f2b3df..551e5d9 100644 --- a/src/RemoteViewer.Client/Controls/DropOverlay.axaml +++ b/src/RemoteViewer.Client/Controls/DropOverlay.axaml @@ -1,18 +1,19 @@ + Padding="{theme:Spacing LG}"> - + - + diff --git a/src/RemoteViewer.Client/Controls/IconBadge.axaml b/src/RemoteViewer.Client/Controls/IconBadge.axaml index 2b71562..a735019 100644 --- a/src/RemoteViewer.Client/Controls/IconBadge.axaml +++ b/src/RemoteViewer.Client/Controls/IconBadge.axaml @@ -6,6 +6,41 @@ x:DataType="controls:IconBadge" x:Name="Root"> + + + + + + + + + + + + + + (32.0, 8.0, 16.0), - IconBadgeSize.Medium => (40.0, 10.0, 22.0), - IconBadgeSize.Large => (64.0, 32.0, 32.0), - _ => (40.0, 10.0, 22.0) - }; - - this.BadgeBorder.Width = badgeSize; - this.BadgeBorder.Height = badgeSize; - this.BadgeBorder.CornerRadius = new CornerRadius(cornerRadius); - this.BadgeIcon.Width = iconSize; - this.BadgeIcon.Height = iconSize; } } diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index 1ad4a11..730ad3b 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -11,7 +11,7 @@ - + @@ -184,7 +184,7 @@ - + - + this.Size = size; + + public override object ProvideValue(IServiceProvider serviceProvider) => (double)this.Size; +} diff --git a/src/RemoteViewer.Client/Themes/GridSpacingExtension.cs b/src/RemoteViewer.Client/Themes/GridSpacingExtension.cs new file mode 100644 index 0000000..22473f2 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/GridSpacingExtension.cs @@ -0,0 +1,16 @@ +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace RemoteViewer.Client.Themes; + +public class GridSpacingExtension : MarkupExtension +{ + public SpacingSize Size { get; set; } = SpacingSize.None; + + public GridSpacingExtension() { } + + public GridSpacingExtension(SpacingSize size) => this.Size = size; + + public override object ProvideValue(IServiceProvider serviceProvider) + => new GridLength((double)this.Size, GridUnitType.Pixel); +} diff --git a/src/RemoteViewer.Client/Themes/Spacing.axaml b/src/RemoteViewer.Client/Themes/Spacing.axaml deleted file mode 100644 index 42e4f45..0000000 --- a/src/RemoteViewer.Client/Themes/Spacing.axaml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - 4 - 8 - 12 - 16 - 24 - 32 - - - 4,0 - 8,0 - 12,0 - 16,0 - 24,0 - - - 0,4 - 0,8 - 0,12 - 0,16 - 0,24 - - - 20 - 24 - 16 - - - 14 - 16 - - - - - - - 2 - 4 - 8 - 12 - 16 - 24 - 32 - - - 6 - 10 - 16 - - diff --git a/src/RemoteViewer.Client/Themes/SpacingExtension.cs b/src/RemoteViewer.Client/Themes/SpacingExtension.cs new file mode 100644 index 0000000..42b1718 --- /dev/null +++ b/src/RemoteViewer.Client/Themes/SpacingExtension.cs @@ -0,0 +1,41 @@ +using Avalonia; +using Avalonia.Markup.Xaml; + +namespace RemoteViewer.Client.Themes; + +public enum SpacingSize +{ + None = 0, + XXS = 2, + XS = 4, + SM = 8, + MD = 12, + LG = 16, + XL = 24, + XXL = 32 +} + +public class SpacingExtension : MarkupExtension +{ + public SpacingSize All { get; set; } = SpacingSize.None; + public SpacingSize? X { get; set; } + public SpacingSize? Y { get; set; } + public SpacingSize? Top { get; set; } + public SpacingSize? Bottom { get; set; } + public SpacingSize? Left { get; set; } + public SpacingSize? Right { get; set; } + + public SpacingExtension() { } + + public SpacingExtension(SpacingSize all) => this.All = all; + + public override object ProvideValue(IServiceProvider serviceProvider) + { + var top = (double)(this.Top ?? this.Y ?? this.All); + var bottom = (double)(this.Bottom ?? this.Y ?? this.All); + var left = (double)(this.Left ?? this.X ?? this.All); + var right = (double)(this.Right ?? this.X ?? this.All); + + return new Thickness(left, top, right, bottom); + } +} diff --git a/src/RemoteViewer.Client/Themes/Utilities.axaml b/src/RemoteViewer.Client/Themes/Utilities.axaml deleted file mode 100644 index 353aeb5..0000000 --- a/src/RemoteViewer.Client/Themes/Utilities.axaml +++ /dev/null @@ -1,745 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/RemoteViewer.Client/Views/About/AboutView.axaml b/src/RemoteViewer.Client/Views/About/AboutView.axaml index 3d0adce..a0f218e 100644 --- a/src/RemoteViewer.Client/Views/About/AboutView.axaml +++ b/src/RemoteViewer.Client/Views/About/AboutView.axaml @@ -4,6 +4,7 @@ xmlns:vm="using:RemoteViewer.Client.Views.About" xmlns:mi="using:Material.Icons.Avalonia" xmlns:mik="using:Material.Icons" + xmlns:theme="using:RemoteViewer.Client.Themes" x:Class="RemoteViewer.Client.Views.About.AboutView" x:DataType="vm:AboutViewModel" Icon="/Assets/avalonia-logo.ico" @@ -15,25 +16,33 @@ DataContextChanged="Window_DataContextChanged" Closed="Window_Closed"> - + + - + - + + Classes="title-large" + HorizontalAlignment="Center"/> + Classes="body-small secondary" + HorizontalAlignment="Center"/> + Classes="small secondary" + HorizontalAlignment="Center" + Margin="{theme:Spacing Top=XS}"/> @@ -41,52 +50,64 @@ - + + VerticalAlignment="Center" + Foreground="{DynamicResource TextSecondaryBrush}"/> + Classes="header-small" + VerticalAlignment="Center"/> - + + Classes="text-badge" + VerticalAlignment="Center"> + Orientation="Horizontal" + Spacing="{theme:Gap LG}" + Margin="{theme:Spacing Top=SM}"> - + Padding="0" + Cursor="Hand"> + + VerticalAlignment="Center"/> + Classes="small" + VerticalAlignment="Center"/> - + Padding="0" + Cursor="Hand"> + + VerticalAlignment="Center"/> + Classes="small" + VerticalAlignment="Center"/> @@ -101,12 +122,15 @@ + diff --git a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml index ec8b1dc..0f61726 100644 --- a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml +++ b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml @@ -4,6 +4,7 @@ xmlns:controls="using:RemoteViewer.Client.Controls" xmlns:hub="using:RemoteViewer.Client.Services.HubClient" xmlns:local="using:RemoteViewer.Client.Views.Chat" + xmlns:theme="using:RemoteViewer.Client.Themes" x:Class="RemoteViewer.Client.Views.Chat.ChatView" x:DataType="local:ChatViewModel" x:Name="ChatWindow" @@ -15,7 +16,7 @@ ShowInTaskbar="True" WindowStartupLocation="CenterOwner"> - + - @@ -40,28 +43,32 @@ + Margin="{theme:Spacing Top=XS}"> diff --git a/src/RemoteViewer.Client/Views/Main/MainView.axaml b/src/RemoteViewer.Client/Views/Main/MainView.axaml index aa6a641..ba711a7 100644 --- a/src/RemoteViewer.Client/Views/Main/MainView.axaml +++ b/src/RemoteViewer.Client/Views/Main/MainView.axaml @@ -6,6 +6,7 @@ xmlns:toastControls="using:RemoteViewer.Client.Controls.Toasts" xmlns:mi="using:Material.Icons.Avalonia" xmlns:mik="using:Material.Icons" + xmlns:theme="using:RemoteViewer.Client.Themes" x:Class="RemoteViewer.Client.Views.Main.MainView" x:DataType="vm:MainViewModel" @@ -14,91 +15,76 @@ Title="Remote Viewer" CanResize="False" + SizeToContent="Height" Width="500" - Height="240" DataContextChanged="Window_DataContextChanged"> - - - - - - - - - - - - - - + - + - + + Classes="header" + Margin="{theme:Spacing Bottom=MD}"/> - + + Classes="credential" + Margin="{theme:Spacing Y=SM}"/> - + - + + Classes="credential" + Margin="{theme:Spacing Y=SM}" + VerticalAlignment="Center"/> - + - - + + + Classes="header"/> - + - + - + - + + + + + + + + + + + + + + HorizontalAlignment="Right" + VerticalAlignment="Bottom" + Margin="{theme:Spacing Right=SM, Bottom=SM}"/> - + VerticalAlignment="Center" + Margin="{theme:Spacing Right=MD}"/> + VerticalAlignment="Center"/> diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 5c36155..7ff7bbe 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -7,6 +7,7 @@ xmlns:chatControls="using:RemoteViewer.Client.Views.Chat" xmlns:mi="using:Material.Icons.Avalonia" xmlns:mik="using:Material.Icons" + xmlns:theme="using:RemoteViewer.Client.Themes" x:Class="RemoteViewer.Client.Views.Presenter.PresenterView" x:DataType="vm:PresenterViewModel" @@ -24,8 +25,11 @@ - + Background="{DynamicResource SurfaceElevatedBrush}" + Padding="{theme:Spacing X=SM, Y=SM}"> + - + @@ -125,38 +137,47 @@ + Classes="header-small" + Margin="{theme:Spacing Bottom=MD}"/> - + + Classes="credential" + Margin="{theme:Spacing Y=SM}"/> - + - + + Classes="credential" + Margin="{theme:Spacing Y=SM}" + VerticalAlignment="Center"/> @@ -170,24 +191,27 @@ + Classes="header-small" + Margin="{theme:Spacing Bottom=MD}"/> - + - + + VerticalAlignment="Center"/> + VerticalAlignment="Center"/> @@ -211,7 +235,9 @@ + HorizontalAlignment="Right" + VerticalAlignment="Bottom" + Margin="{theme:Spacing Right=SM, Bottom=SM}"/> @@ -59,7 +61,9 @@ - + - + @@ -211,10 +222,12 @@ + HorizontalAlignment="Left" + VerticalAlignment="Center" + Margin="{theme:Spacing X=LG}"> - + @@ -240,10 +253,12 @@ + HorizontalAlignment="Right" + VerticalAlignment="Center" + Margin="{theme:Spacing X=LG}"> - + @@ -269,10 +284,12 @@ + HorizontalAlignment="Center" + VerticalAlignment="Top" + Margin="{theme:Spacing Y=LG}"> - + @@ -298,10 +315,12 @@ + HorizontalAlignment="Center" + VerticalAlignment="Bottom" + Margin="{theme:Spacing Y=LG}"> - + @@ -330,7 +349,9 @@ + HorizontalAlignment="Right" + VerticalAlignment="Bottom" + Margin="{theme:Spacing Right=SM, Bottom=SM}"/> Date: Sun, 11 Jan 2026 15:49:38 +0100 Subject: [PATCH 03/14] Apply design system tokens consistently across all UI files Replace hardcoded values with design tokens throughout: - Icon sizes: IconSizeSM, IconSizeMD, IconSizeXL - Corner radii: CornerRadiusSM, CornerRadiusMD, CornerRadiusLG, CornerRadiusButton - Spacing: {theme:Spacing} and {theme:Gap} markup extensions - Typography: body-small, caption, small classes - Colors: TextSecondaryBrush (replacing old Avalonia system colors) Co-Authored-By: Claude Opus 4.5 --- .../Controls/DisplayMiniMap.axaml | 2 +- .../Controls/Toasts/ToastsView.axaml | 111 +++++++++--------- .../Views/Chat/ChatView.axaml | 13 +- .../Views/Main/MainView.axaml | 21 +++- .../Views/Presenter/PresenterView.axaml | 30 +++-- .../Views/Viewer/ViewerView.axaml | 63 +++++++--- 6 files changed, 145 insertions(+), 95 deletions(-) diff --git a/src/RemoteViewer.Client/Controls/DisplayMiniMap.axaml b/src/RemoteViewer.Client/Controls/DisplayMiniMap.axaml index 3b665d9..50b0b02 100644 --- a/src/RemoteViewer.Client/Controls/DisplayMiniMap.axaml +++ b/src/RemoteViewer.Client/Controls/DisplayMiniMap.axaml @@ -14,7 +14,7 @@ diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index 730ad3b..dc6b4b7 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -5,24 +5,25 @@ xmlns:toasts="using:RemoteViewer.Client.Controls.Toasts" xmlns:ft="using:RemoteViewer.Client.Services.FileTransfer" xmlns:conv="using:RemoteViewer.Client.Converters" + xmlns:theme="using:RemoteViewer.Client.Themes" x:Class="RemoteViewer.Client.Controls.Toasts.ToastsView" x:DataType="toasts:ToastsViewModel"> - + - - @@ -46,12 +47,12 @@ - @@ -85,22 +86,22 @@ VerticalAlignment="Center" TextWrapping="Wrap" Foreground="{DynamicResource ToastForegroundBrush}" - FontSize="13" + Classes="body-small" FontWeight="SemiBold" - Margin="0,14,8,14"/> + Margin="{theme:Spacing Y=MD, Right=SM}"/> @@ -122,11 +123,11 @@ - - @@ -150,12 +151,12 @@ - @@ -184,16 +185,16 @@ - + @@ -261,11 +262,11 @@ - - @@ -275,20 +276,20 @@ Background="{StaticResource ToastTransferAccentBrush}"/> - + @@ -299,18 +300,18 @@ VerticalAlignment="Center" Foreground="{DynamicResource ToastForegroundBrush}" FontWeight="SemiBold" - FontSize="13"/> + Classes="body-small"/> @@ -333,8 +334,8 @@ Text="{Binding Transfer.FileName}" TextTrimming="CharacterEllipsis" Foreground="{DynamicResource ToastSecondaryTextBrush}" - FontSize="12" - Margin="0,8,0,10" + Classes="caption" + Margin="{theme:Spacing Top=SM, Bottom=SM}" ToolTip.Tip="{Binding Transfer.FileName}"/> @@ -361,9 +362,9 @@ + Margin="{theme:Spacing Top=SM}"> @@ -378,7 +379,7 @@ + Margin="{theme:Spacing Top=SM}"> @@ -391,12 +392,12 @@ + Classes="small"/> diff --git a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml index 0f61726..fce7074 100644 --- a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml +++ b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml @@ -27,7 +27,7 @@ @@ -50,8 +50,7 @@ + Classes="body-small"/> - + diff --git a/src/RemoteViewer.Client/Views/Main/MainView.axaml b/src/RemoteViewer.Client/Views/Main/MainView.axaml index ba711a7..ceb6063 100644 --- a/src/RemoteViewer.Client/Views/Main/MainView.axaml +++ b/src/RemoteViewer.Client/Views/Main/MainView.axaml @@ -49,7 +49,9 @@ Margin="{theme:Spacing Left=SM}" Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> - @@ -68,8 +70,10 @@ ToolTip.Tip="Generate new password" Classes="icon-button" VerticalAlignment="Center" - Width="28" Height="28"> - + @@ -109,7 +113,9 @@ Classes="icon-button" Padding="{theme:Spacing XS}" IsEnabled="{Binding !ConnectToDeviceCommand.IsRunning}"> - @@ -138,7 +144,9 @@ ToolTip.Tip="About" Classes="icon-button" Width="24" Height="24"> - @@ -158,7 +166,8 @@ IsVisible="{Binding HasVersionMismatch}"> diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 7ff7bbe..d0f2fb2 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -36,7 +36,9 @@ Classes="icon-button" IsEnabled="{Binding Viewers.Count}" Command="{Binding SendFileCommand}"> - + @@ -45,7 +47,9 @@ Classes="icon-button" Command="{Binding ShowChatCommand}"> - + - + @@ -120,7 +126,9 @@ @@ -156,7 +164,9 @@ Margin="{theme:Spacing Left=SM}" Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> - @@ -175,8 +185,10 @@ ToolTip.Tip="Generate new password" Classes="icon-button" VerticalAlignment="Center" - Width="28" Height="28"> - + @@ -220,8 +232,8 @@ ToolTip.Tip="{Binding IsInputBlocked, Converter={conv:BoolToObject TrueValue='Unblock input', FalseValue='Block input'}}" Classes="icon-button"> + Width="{StaticResource IconSizeSM}" + Height="{StaticResource IconSizeSM}"/> diff --git a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml index 9930095..27a0880 100644 --- a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml +++ b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml @@ -69,7 +69,8 @@ ToolTip.Tip="{Binding IsFullscreen, Converter={conv:BoolToObject TrueValue='Exit fullscreen (F11 / ESC)', FalseValue='Enter fullscreen (F11)'}}" Classes="icon-button"> + Width="{StaticResource IconSizeMD}" + Height="{StaticResource IconSizeMD}"/> @@ -89,7 +90,9 @@ - + @@ -103,7 +106,8 @@ + Width="{StaticResource IconSizeMD}" + Height="{StaticResource IconSizeMD}"/> @@ -117,14 +121,18 @@ - + @@ -145,11 +153,12 @@ @@ -160,7 +169,9 @@ - + @@ -169,7 +180,9 @@ Classes="icon-button" Command="{Binding ShowChatCommand}"> - + - @@ -237,7 +252,7 @@ Width="48" Height="80" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="8" + CornerRadius="{StaticResource CornerRadiusMD}" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Left)'}"> @@ -245,7 +260,10 @@ - + @@ -268,7 +286,7 @@ Width="48" Height="80" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="8" + CornerRadius="{StaticResource CornerRadiusMD}" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Right)'}"> @@ -276,7 +294,10 @@ - + @@ -299,7 +320,7 @@ Width="80" Height="48" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="8" + CornerRadius="{StaticResource CornerRadiusMD}" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Up)'}"> @@ -307,7 +328,10 @@ - + @@ -330,7 +354,7 @@ Width="80" Height="48" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="8" + CornerRadius="{StaticResource CornerRadiusMD}" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Down)'}"> @@ -338,7 +362,10 @@ - + From eef89b4a42eba6c7bd094ad260c6b5fe430a1c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Sun, 11 Jan 2026 15:59:48 +0100 Subject: [PATCH 04/14] Document design system in CLAUDE.md Add comprehensive reference for markup extensions, design tokens, typography classes, button styles, and reusable components. Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index b9ff345..4702535 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,4 +60,106 @@ For detailed MVVM patterns, examples, and best practices, see the `avalonia-mvvm - **Dependencies**: `[NotifyPropertyChangedFor]` and `[NotifyCanExecuteChangedFor]` - **Bindings**: Use `x:DataType` in XAML for compiled bindings - **DI**: Services registered in `ServiceRegistration.cs` -- **Threading**: Use `IDispatcher` for UI updates from background threads \ No newline at end of file +- **Threading**: Use `IDispatcher` for UI updates from background threads + +# Design System (Client) + +The client uses a comprehensive design system with markup extensions, design tokens, and reusable components. All UI should use these tokens instead of hardcoded values. + +## Markup Extensions + +Located in `Themes/` folder. Add `xmlns:theme="using:RemoteViewer.Client.Themes"` to use. + +### Spacing (Margin/Padding) +```xml +Padding="{theme:Spacing MD}" +Margin="{theme:Spacing X=LG, Y=SM}" +Margin="{theme:Spacing Top=XL, Right=MD}" +``` + +Values: `None=0, XXS=2, XS=4, SM=8, MD=12, LG=16, XL=24, XXL=32` + +### Gap (StackPanel/ItemsControl Spacing) +```xml + + +``` + +Values: `None=0, XXS=2, XS=4, Icon=6, SM=8, MD=12, LG=16, XL=24, XXL=32` + +### GridSpacing (Grid Column/Row Definitions) +```xml + +``` + +## Design Tokens + +### Icon Sizes +```xml +Width="{StaticResource IconSizeXS}" +Width="{StaticResource IconSizeSM}" +Width="{StaticResource IconSizeMD}" +Width="{StaticResource IconSizeLG}" +Width="{StaticResource IconSizeXL}" +``` + +### Corner Radii +```xml +CornerRadius="{StaticResource CornerRadiusSM}" +CornerRadius="{StaticResource CornerRadiusMD}" +CornerRadius="{StaticResource CornerRadiusLG}" +CornerRadius="{StaticResource CornerRadiusXL}" +CornerRadius="{StaticResource CornerRadiusButton}" +CornerRadius="{StaticResource CornerRadiusCard}" +``` + +### Colors (use DynamicResource for theme support) +- **Text**: `TextPrimaryBrush`, `TextSecondaryBrush`, `TextMutedBrush`, `TextDisabledBrush` +- **Surfaces**: `SurfaceBrush`, `SurfaceElevatedBrush`, `CardBackgroundBrush` +- **Borders**: `BorderSubtleBrush`, `BorderDefaultBrush`, `BorderStrongBrush` +- **Semantic**: `AccentBrush`, `SuccessBrush`, `ErrorBrush`, `WarningBrush`, `InfoBrush` + +## Typography Classes + +Apply via `Classes="class-name"` on TextBlock: + +- **Titles**: `title-large` (22px Bold), `title` (20px SemiBold) +- **Headers**: `header` (15px SemiBold), `header-small` (13px SemiBold) +- **Body**: `body` (14px), `body-small` (13px) +- **Small**: `caption` (12px), `small` (11px), `small-muted` (10px) +- **Special**: `credential` (18px Bold monospace), `monospace` (13px) +- **Color modifiers**: `secondary`, `muted`, `accent`, `success`, `error`, `warning` + +## Button Styles + +```xml + - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + - - - - - - - + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + - + + + + diff --git a/src/RemoteViewer.Client/Views/Main/MainViewModel.cs b/src/RemoteViewer.Client/Views/Main/MainViewModel.cs index f4a4f86..d813721 100644 --- a/src/RemoteViewer.Client/Views/Main/MainViewModel.cs +++ b/src/RemoteViewer.Client/Views/Main/MainViewModel.cs @@ -1,4 +1,4 @@ -using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Extensions.Logging; using RemoteViewer.Client.Controls.Toasts; @@ -34,17 +34,27 @@ public partial class MainViewModel : ViewModelBase [ObservableProperty] private string? _targetPassword; - [ObservableProperty] - private bool _isConnected; - - [ObservableProperty] - private string _statusText = "Connecting..."; + public ConnectionStatus Status => this._hubClient switch + { + { HasVersionMismatch: true } => ConnectionStatus.VersionMismatch, + { IsConnected: true } => ConnectionStatus.Connected, + _ => ConnectionStatus.Connecting + }; - [ObservableProperty] - private bool _hasVersionMismatch; + public string? VersionTooltipText + { + get + { + if (this._hubClient.HasVersionMismatch is false) + return null; - [ObservableProperty] - private string _versionMismatchText = string.Empty; + return $""" + Version Mismatch + Server: v{this._hubClient.ServerVersion} + Client: v{ThisAssembly.AssemblyInformationalVersion} + """; + } + } public event EventHandler? RequestHideMainView; public event EventHandler? RequestShowMainView; @@ -62,21 +72,10 @@ public MainViewModel(ConnectionHubClient hubClient, IDispatcher dispatcher, IVie { this._dispatcher.Post(() => { - this.IsConnected = this._hubClient.IsConnected; - this.HasVersionMismatch = this._hubClient.HasVersionMismatch; - - if (this._hubClient.HasVersionMismatch) - { - this.VersionMismatchText = $""" - Version mismatch! - Server v{this._hubClient.ServerVersion} - Client v{ThisAssembly.AssemblyInformationalVersion} - """; - } - - this.StatusText = this._hubClient.IsConnected ? "Connected" : "Connecting..."; + this.OnPropertyChanged(nameof(this.Status)); + this.OnPropertyChanged(nameof(this.VersionTooltipText)); - this._logger.HubConnectionStatusChanged(this._hubClient.IsConnected, this.StatusText); + this._logger.HubConnectionStatusChanged(this._hubClient.IsConnected, this.Status.ToString()); this.YourUsername = this._hubClient.IsConnected ? this._hubClient.Username : "..."; this.YourPassword = this._hubClient.IsConnected ? this._hubClient.Password : "..."; @@ -93,58 +92,22 @@ Client v{ThisAssembly.AssemblyInformationalVersion} }); }; - this.IsConnected = this._hubClient.IsConnected; - - // Handle viewer connections - open viewer window when connected as viewer - this._hubClient.ConnectionStarted += this.OnConnectionStarted; - } - - private void OnConnectionStarted(object? sender, ConnectionStartedEventArgs e) - { - this._dispatcher.Post(() => + this._hubClient.ConnectionStarted += (_, e) => { - if (e.Connection.IsPresenter) - { - this._logger.ConnectionSuccessful("Presenter"); - this.OpenPresenterWindow(e.Connection); - } - else + this._dispatcher.Post(() => { - this._logger.ConnectionSuccessful("Viewer"); - this.OpenViewerWindow(e.Connection); - } - }); - } - - private void OpenPresenterWindow(Connection connection) - { - this.RequestHideMainView?.Invoke(this, EventArgs.Empty); - - var viewModel = this._viewModelFactory.CreatePresenterViewModel(connection); - this._sessionWindowHandle = this._dialogService.ShowPresenterWindow(viewModel); - this._sessionWindowHandle.Closed += this.OnSessionWindowClosed; - } - - private void OpenViewerWindow(Connection connection) - { - this.RequestHideMainView?.Invoke(this, EventArgs.Empty); - - var viewModel = this._viewModelFactory.CreateViewerViewModel(connection); - this._sessionWindowHandle = this._dialogService.ShowViewerWindow(viewModel); - this._sessionWindowHandle.Closed += this.OnSessionWindowClosed; - } - - private void OnSessionWindowClosed(object? sender, EventArgs e) - { - this._logger.SessionWindowClosed(); - - if (this._sessionWindowHandle is not null) - { - this._sessionWindowHandle.Closed -= this.OnSessionWindowClosed; - this._sessionWindowHandle = null; - } - - this.RequestShowMainView?.Invoke(this, EventArgs.Empty); + if (e.Connection.IsPresenter) + { + this._logger.ConnectionSuccessful("Presenter"); + this.OpenPresenterWindow(e.Connection); + } + else + { + this._logger.ConnectionSuccessful("Viewer"); + this.OpenViewerWindow(e.Connection); + } + }); + }; } [RelayCommand] @@ -206,4 +169,31 @@ private async Task ShowAboutAsync() { await this._dialogService.ShowAboutDialogAsync(); } + + private void OpenPresenterWindow(Connection connection) + { + this.RequestHideMainView?.Invoke(this, EventArgs.Empty); + + var viewModel = this._viewModelFactory.CreatePresenterViewModel(connection); + this._sessionWindowHandle = this._dialogService.ShowPresenterWindow(viewModel); + this._sessionWindowHandle.Closed += this.OnSessionWindowClosed; + } + + private void OpenViewerWindow(Connection connection) + { + this.RequestHideMainView?.Invoke(this, EventArgs.Empty); + + var viewModel = this._viewModelFactory.CreateViewerViewModel(connection); + this._sessionWindowHandle = this._dialogService.ShowViewerWindow(viewModel); + this._sessionWindowHandle.Closed += this.OnSessionWindowClosed; + } + + private void OnSessionWindowClosed(object? sender, EventArgs e) + { + this._logger.SessionWindowClosed(); + this._sessionWindowHandle?.Closed -= this.OnSessionWindowClosed; + this._sessionWindowHandle = null; + + this.RequestShowMainView?.Invoke(this, EventArgs.Empty); + } } diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index d0f2fb2..4f05b0e 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -140,7 +140,7 @@ Margin="{theme:Spacing LG}"> - + - + - @@ -241,8 +241,8 @@ - - + + Date: Mon, 12 Jan 2026 23:03:34 +0100 Subject: [PATCH 06/14] Consolidate typography styles to h1/h2/h3, m1/m2 naming Simplify typography system from 11 classes to 7: - h1, h2, h3: Headings (22px, 15px, 13px) - m1, m2: Small text (12px, 10px) - credential: Monospace for IDs/passwords - muted: Color modifier Removed: title, title-large, header, header-small, body-small, caption, small, small-muted, secondary Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 10 +- .../Controls/DialogHeader.axaml | 4 +- .../FileTransferConfirmationDialog.axaml | 6 +- .../Dialogs/ViewerSelectionDialog.axaml | 7 +- .../Controls/DropOverlay.axaml | 4 +- .../Controls/Toasts/ToastsView.axaml | 15 +- .../Themes/Typography.axaml | 94 ++------ .../Views/About/AboutView.axaml | 212 +++++++++--------- .../Views/Chat/ChatView.axaml | 18 +- .../Views/Main/MainView.axaml | 14 +- .../Views/Presenter/PresenterView.axaml | 19 +- .../Views/Viewer/ViewerView.axaml | 5 +- 12 files changed, 171 insertions(+), 237 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0aa5b38..30cb1b2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -123,12 +123,10 @@ CornerRadius="{StaticResource CornerRadiusCard}" Apply via `Classes="class-name"` on TextBlock: -- **Titles**: `title-large` (22px Bold), `title` (20px SemiBold) -- **Headers**: `header` (15px SemiBold), `header-small` (13px SemiBold) -- **Body**: `body` (14px), `body-small` (13px) -- **Small**: `caption` (12px), `small` (11px), `small-muted` (10px) -- **Special**: `credential` (18px Bold monospace), `monospace` (13px) -- **Color modifiers**: `secondary`, `muted`, `accent`, `success`, `error`, `warning` +- **Headings**: `h1` (22px Bold), `h2` (15px SemiBold), `h3` (13px SemiBold) +- **Small text**: `m1` (12px), `m2` (10px) +- **Special**: `credential` (18px Bold monospace) +- **Color modifier**: `muted` ## Button Styles diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml b/src/RemoteViewer.Client/Controls/DialogHeader.axaml index bf33280..c362413 100644 --- a/src/RemoteViewer.Client/Controls/DialogHeader.axaml +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml @@ -21,9 +21,9 @@ VerticalAlignment="Center" Spacing="{theme:Gap XS}"> + Classes="h2"/> diff --git a/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml index 0ad0ab5..c782f7f 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml @@ -11,7 +11,7 @@ SizeToContent="WidthAndHeight" CanResize="False" ShowInTaskbar="False" - WindowStartupLocation="CenterScreen"> + WindowStartupLocation="CenterOwner"> @@ -36,11 +36,11 @@ Foreground="{DynamicResource TextSecondaryBrush}"/> + Classes="m1"/> diff --git a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml index 8c61f58..8e198a6 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml @@ -39,11 +39,11 @@ Foreground="{DynamicResource TextSecondaryBrush}"/> + Classes="m1"/> @@ -51,7 +51,7 @@ @@ -68,7 +68,6 @@ VerticalAlignment="Center" Foreground="{DynamicResource TextSecondaryBrush}"/> diff --git a/src/RemoteViewer.Client/Controls/DropOverlay.axaml b/src/RemoteViewer.Client/Controls/DropOverlay.axaml index e001f91..dd4aff9 100644 --- a/src/RemoteViewer.Client/Controls/DropOverlay.axaml +++ b/src/RemoteViewer.Client/Controls/DropOverlay.axaml @@ -30,10 +30,10 @@ diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index dc6b4b7..160535a 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -86,7 +86,6 @@ VerticalAlignment="Center" TextWrapping="Wrap" Foreground="{DynamicResource ToastForegroundBrush}" - Classes="body-small" FontWeight="SemiBold" Margin="{theme:Spacing Y=MD, Right=SM}"/> @@ -189,7 +188,6 @@ - + + + diff --git a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml index fce7074..e781773 100644 --- a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml +++ b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml @@ -43,14 +43,13 @@ + TextWrapping="Wrap"/> - - + @@ -77,7 +76,7 @@ @@ -89,16 +88,17 @@ - + - diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 4f05b0e..6b7808e 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -16,7 +16,7 @@ Width="300" CanResize="False" SizeToContent="Height" - WindowStartupLocation="CenterScreen" + WindowStartupLocation="CenterOwner" DragDrop.AllowDrop="True" DataContextChanged="Window_DataContextChanged" @@ -29,7 +29,8 @@ Padding="{theme:Spacing X=SM, Y=SM}"> + VerticalAlignment="Center" + HorizontalAlignment="Center"> @@ -68,7 +67,7 @@ HorizontalAlignment="Stretch" Click="OnAcceptClicked"> - + diff --git a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml index 8e198a6..7320f36 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml @@ -32,8 +32,7 @@ @@ -63,8 +62,7 @@ Cursor="Hand"> - + @@ -100,7 +98,7 @@ HorizontalAlignment="Stretch" Click="OnSendClicked"> - + diff --git a/src/RemoteViewer.Client/Controls/DropOverlay.axaml b/src/RemoteViewer.Client/Controls/DropOverlay.axaml index dd4aff9..603ca11 100644 --- a/src/RemoteViewer.Client/Controls/DropOverlay.axaml +++ b/src/RemoteViewer.Client/Controls/DropOverlay.axaml @@ -21,7 +21,7 @@ Width="64" Height="64" HorizontalAlignment="Center"> diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index 160535a..613ba6f 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -19,11 +19,11 @@ - - @@ -47,12 +47,11 @@ - @@ -100,7 +99,7 @@ Margin="{theme:Spacing Right=SM}" VerticalAlignment="Center" Cursor="Hand" - CornerRadius="{StaticResource CornerRadiusButton}"> + CornerRadius="6"> @@ -122,11 +120,11 @@ - - @@ -150,12 +148,11 @@ - @@ -191,7 +188,7 @@ FontWeight="SemiBold"/> @@ -260,11 +255,11 @@ - - @@ -281,13 +276,12 @@ @@ -308,7 +302,7 @@ Padding="0" VerticalAlignment="Center" Cursor="Hand" - CornerRadius="{StaticResource CornerRadiusButton}" + CornerRadius="6" ToolTip.Tip="Cancel transfer"> diff --git a/src/RemoteViewer.Client/Themes/Borders.axaml b/src/RemoteViewer.Client/Themes/Borders.axaml deleted file mode 100644 index ce108c6..0000000 --- a/src/RemoteViewer.Client/Themes/Borders.axaml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - 0 - 4 - 8 - 12 - 20 - 32 - - - 6 - 8 - 12 - - - - - - 12 - 16 - 20 - 24 - 32 - - diff --git a/src/RemoteViewer.Client/Themes/Colors.axaml b/src/RemoteViewer.Client/Themes/Colors.axaml index c1970d6..aefee56 100644 --- a/src/RemoteViewer.Client/Themes/Colors.axaml +++ b/src/RemoteViewer.Client/Themes/Colors.axaml @@ -94,8 +94,8 @@ - - + + @@ -143,8 +143,8 @@ - - + + diff --git a/src/RemoteViewer.Client/Themes/IconStyles.axaml b/src/RemoteViewer.Client/Themes/IconStyles.axaml new file mode 100644 index 0000000..030b11c --- /dev/null +++ b/src/RemoteViewer.Client/Themes/IconStyles.axaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Views/About/AboutView.axaml b/src/RemoteViewer.Client/Views/About/AboutView.axaml index 1daa02a..6e8764b 100644 --- a/src/RemoteViewer.Client/Views/About/AboutView.axaml +++ b/src/RemoteViewer.Client/Views/About/AboutView.axaml @@ -55,8 +55,7 @@ Spacing="{theme:Gap SM}" Margin="{theme:Spacing Bottom=MD}"> - + diff --git a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml index e781773..37efbc8 100644 --- a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml +++ b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml @@ -27,7 +27,7 @@ @@ -62,7 +62,7 @@ Margin="{theme:Spacing Top=XXS}" HorizontalAlignment="Left"> - + diff --git a/src/RemoteViewer.Client/Views/Main/MainView.axaml b/src/RemoteViewer.Client/Views/Main/MainView.axaml index f2ea8f9..9bb37a2 100644 --- a/src/RemoteViewer.Client/Views/Main/MainView.axaml +++ b/src/RemoteViewer.Client/Views/Main/MainView.axaml @@ -1,4 +1,4 @@ - @@ -91,8 +90,7 @@ Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> @@ -145,8 +143,7 @@ Classes="icon-button" IsEnabled="{Binding !ConnectToDeviceCommand.IsRunning}"> @@ -163,8 +160,7 @@ - + @@ -208,8 +204,7 @@ Width="24" Height="24"> diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 6b7808e..1c85023 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -38,8 +38,7 @@ IsEnabled="{Binding Viewers.Count}" Command="{Binding SendFileCommand}"> + Classes="icon-sm"/> @@ -49,8 +48,7 @@ Command="{Binding ShowChatCommand}"> + Classes="icon-sm"/> + Classes="icon-sm"/> @@ -128,8 +125,7 @@ ToolTip.Tip="Stop presenting" Classes="icon-button"> @@ -166,8 +162,7 @@ Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> @@ -188,8 +183,7 @@ VerticalAlignment="Center" Width="32" Height="32"> @@ -219,8 +213,7 @@ Orientation="Horizontal" Spacing="{theme:Gap SM}"> + Classes="icon-xs"/> diff --git a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml index 66862da..97010d2 100644 --- a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml +++ b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml @@ -70,8 +70,7 @@ ToolTip.Tip="{Binding IsFullscreen, Converter={conv:BoolToObject TrueValue='Exit fullscreen (F11 / ESC)', FalseValue='Enter fullscreen (F11)'}}" Classes="icon-button"> + Classes="icon-sm"/> @@ -92,8 +91,7 @@ + Classes="icon-sm"/> @@ -107,8 +105,7 @@ + Classes="icon-sm"/> @@ -123,8 +120,7 @@ + Classes="icon-sm"/> @@ -132,8 +128,7 @@ ToolTip.Tip="Send file to presenter" Classes="icon-button"> + Classes="icon-sm"/> @@ -154,12 +149,11 @@ @@ -171,8 +165,7 @@ + Classes="icon-sm"/> @@ -182,8 +175,7 @@ Command="{Binding ShowChatCommand}"> + Classes="icon-sm"/> @@ -253,7 +244,7 @@ Width="48" Height="80" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="{StaticResource CornerRadiusMD}" + CornerRadius="8" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Left)'}"> @@ -262,8 +253,7 @@ @@ -287,7 +277,7 @@ Width="48" Height="80" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="{StaticResource CornerRadiusMD}" + CornerRadius="8" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Right)'}"> @@ -296,8 +286,7 @@ @@ -321,7 +310,7 @@ Width="80" Height="48" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="{StaticResource CornerRadiusMD}" + CornerRadius="8" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Up)'}"> @@ -330,8 +319,7 @@ @@ -355,7 +343,7 @@ Width="80" Height="48" Background="{DynamicResource OverlayButtonBrush}" BorderThickness="0" - CornerRadius="{StaticResource CornerRadiusMD}" + CornerRadius="8" Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Down)'}"> @@ -364,8 +352,7 @@ From ed9746946652653e9ef45d1b85c1889be78f8dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Tue, 13 Jan 2026 13:31:23 +0100 Subject: [PATCH 08/14] Consolidate markup extensions into unified SpacingExtension - Enhance SpacingExtension to auto-detect target property type and return double, GridLength, or Thickness - Remove redundant GapExtension and GridSpacingExtension - Update all XAML files to use unified {theme:Spacing} syntax - Replace Gap.Icon (6px) with Gap.SM (8px) for icon+text pairs - Maintain backward compatibility for all spacing use cases --- .../Controls/DialogHeader.axaml | 2 +- .../FileTransferConfirmationDialog.axaml | 10 +++--- .../Dialogs/ViewerSelectionDialog.axaml | 14 ++++---- .../Controls/DropOverlay.axaml | 4 +-- .../Controls/Toasts/ToastsView.axaml | 6 ++-- .../Themes/GapExtension.cs | 27 ---------------- .../Themes/GridSpacingExtension.cs | 16 ---------- .../Themes/SpacingExtension.cs | 32 ++++++++++++++++--- .../Views/About/AboutView.axaml | 14 ++++---- .../Views/Chat/ChatView.axaml | 4 +-- .../Views/Main/MainView.axaml | 22 ++++++------- .../Views/Presenter/PresenterView.axaml | 18 +++++------ .../Views/Viewer/ViewerView.axaml | 16 +++++----- 13 files changed, 82 insertions(+), 103 deletions(-) delete mode 100644 src/RemoteViewer.Client/Themes/GapExtension.cs delete mode 100644 src/RemoteViewer.Client/Themes/GridSpacingExtension.cs diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml b/src/RemoteViewer.Client/Controls/DialogHeader.axaml index c362413..37426fd 100644 --- a/src/RemoteViewer.Client/Controls/DialogHeader.axaml +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml @@ -19,7 +19,7 @@ + Spacing="{theme:Spacing XS}"> - + - + - + @@ -56,7 +56,7 @@ Classes="action-secondary" HorizontalAlignment="Stretch" Click="OnRejectClicked"> - + @@ -66,7 +66,7 @@ Classes="action-primary" HorizontalAlignment="Stretch" Click="OnAcceptClicked"> - + diff --git a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml index 7320f36..af97441 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml @@ -18,7 +18,7 @@ MinWidth="320" MaxWidth="400"> - + - + - + - + - + @@ -87,7 +87,7 @@ Classes="action-secondary" HorizontalAlignment="Stretch" Click="OnCancelClicked"> - + @@ -97,7 +97,7 @@ Classes="action-primary" HorizontalAlignment="Stretch" Click="OnSendClicked"> - + diff --git a/src/RemoteViewer.Client/Controls/DropOverlay.axaml b/src/RemoteViewer.Client/Controls/DropOverlay.axaml index 603ca11..d05d284 100644 --- a/src/RemoteViewer.Client/Controls/DropOverlay.axaml +++ b/src/RemoteViewer.Client/Controls/DropOverlay.axaml @@ -14,7 +14,7 @@ VerticalAlignment="Center" Size="XLarge"> - + - + diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index 613ba6f..5cf2af4 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -12,7 +12,7 @@ - + @@ -181,7 +181,7 @@ - + - + this.Size = size; - - public override object ProvideValue(IServiceProvider serviceProvider) => (double)this.Size; -} diff --git a/src/RemoteViewer.Client/Themes/GridSpacingExtension.cs b/src/RemoteViewer.Client/Themes/GridSpacingExtension.cs deleted file mode 100644 index 22473f2..0000000 --- a/src/RemoteViewer.Client/Themes/GridSpacingExtension.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -namespace RemoteViewer.Client.Themes; - -public class GridSpacingExtension : MarkupExtension -{ - public SpacingSize Size { get; set; } = SpacingSize.None; - - public GridSpacingExtension() { } - - public GridSpacingExtension(SpacingSize size) => this.Size = size; - - public override object ProvideValue(IServiceProvider serviceProvider) - => new GridLength((double)this.Size, GridUnitType.Pixel); -} diff --git a/src/RemoteViewer.Client/Themes/SpacingExtension.cs b/src/RemoteViewer.Client/Themes/SpacingExtension.cs index 42b1718..0c7a935 100644 --- a/src/RemoteViewer.Client/Themes/SpacingExtension.cs +++ b/src/RemoteViewer.Client/Themes/SpacingExtension.cs @@ -1,4 +1,6 @@ +using System.Reflection; using Avalonia; +using Avalonia.Controls; using Avalonia.Markup.Xaml; namespace RemoteViewer.Client.Themes; @@ -31,11 +33,31 @@ public SpacingExtension() { } public override object ProvideValue(IServiceProvider serviceProvider) { - var top = (double)(this.Top ?? this.Y ?? this.All); - var bottom = (double)(this.Bottom ?? this.Y ?? this.All); - var left = (double)(this.Left ?? this.X ?? this.All); - var right = (double)(this.Right ?? this.X ?? this.All); + var target = serviceProvider.GetService(typeof(IProvideValueTarget)) as IProvideValueTarget; + var targetProperty = target?.TargetProperty; - return new Thickness(left, top, right, bottom); + Type? targetType = null; + if (targetProperty is PropertyInfo pi) + targetType = pi.PropertyType; + else if (targetProperty is AvaloniaProperty ap) + targetType = ap.PropertyType; + + if (targetType == typeof(double)) + return (double)this.All; + + if (targetType == typeof(GridLength)) + return new GridLength((double)this.All, GridUnitType.Pixel); + + if (targetType == typeof(Thickness)) + { + var top = (double)(this.Top ?? this.Y ?? this.All); + var bottom = (double)(this.Bottom ?? this.Y ?? this.All); + var left = (double)(this.Left ?? this.X ?? this.All); + var right = (double)(this.Right ?? this.X ?? this.All); + + return new Thickness(left, top, right, bottom); + } + + throw new InvalidOperationException($"Cannot convert SpacingExtension to target type '{targetType}'"); } } diff --git a/src/RemoteViewer.Client/Views/About/AboutView.axaml b/src/RemoteViewer.Client/Views/About/AboutView.axaml index 6e8764b..6c00446 100644 --- a/src/RemoteViewer.Client/Views/About/AboutView.axaml +++ b/src/RemoteViewer.Client/Views/About/AboutView.axaml @@ -21,7 +21,7 @@ @@ -30,7 +30,7 @@ BadgeBackground="{DynamicResource AccentBrush}" IconForeground="White"/> - + @@ -52,7 +52,7 @@ - + @@ -98,7 +98,7 @@ - + @@ -123,7 +123,7 @@ HorizontalAlignment="Stretch" Margin="{theme:Spacing Top=XL}" Command="{Binding CloseCommand}"> - + diff --git a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml index 37efbc8..15d88f4 100644 --- a/src/RemoteViewer.Client/Views/Chat/ChatView.axaml +++ b/src/RemoteViewer.Client/Views/Chat/ChatView.axaml @@ -61,7 +61,7 @@ Padding="{theme:Spacing X=SM, Y=XS}" Margin="{theme:Spacing Top=XXS}" HorizontalAlignment="Left"> - + @@ -95,7 +95,7 @@ + Spacing="{theme:Spacing XS}"> - + @@ -37,13 +37,13 @@ - + - + - + - + @@ -66,7 +66,7 @@ + Spacing="{theme:Spacing XS}"> @@ -101,13 +101,13 @@ - + - + - + - + @@ -158,7 +158,7 @@ Padding="{theme:Spacing X=LG, Y=XXS}"> diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 1c85023..8085142 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -28,7 +28,7 @@ Background="{DynamicResource SurfaceElevatedBrush}" Padding="{theme:Spacing X=SM, Y=SM}"> @@ -64,13 +64,13 @@ Classes="icon-button"> - + - + - + @@ -146,7 +146,7 @@ Margin="{theme:Spacing Bottom=MD}"/> - + - + + Spacing="{theme:Spacing SM}"> diff --git a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml index 97010d2..ad651e8 100644 --- a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml +++ b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml @@ -62,7 +62,7 @@ @@ -79,7 +79,7 @@ Classes="icon-button"> - + @@ -141,7 +141,7 @@ Classes="icon-button"> - + @@ -151,7 +151,7 @@ - + @@ -234,7 +234,7 @@ Margin="{theme:Spacing X=LG}"> - + @@ -267,7 +267,7 @@ Margin="{theme:Spacing X=LG}"> - + @@ -300,7 +300,7 @@ Margin="{theme:Spacing Y=LG}"> - + @@ -333,7 +333,7 @@ Margin="{theme:Spacing Y=LG}"> - + From 146e5f00456fbc912c842a3a588d3f8914f5d154 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Tue, 13 Jan 2026 15:07:01 +0100 Subject: [PATCH 09/14] Create unified Icon control with Size property and ShowAsBadge - Add Controls/Icon.axaml and Controls/Icon.axaml.cs - Icon wraps MaterialIcon with Size enum (XXS-XXL) and ShowAsBadge bool - Supports element property syntax via Binding elements for Kind and Foreground - ShowAsBadge=true: Badge mode with configurable size/color - ShowAsBadge=false: Direct icon mode Migrate all MaterialIcon usages to controls:Icon: - AboutView, DialogHeader, MainView, PresenterView, ViewerView - ChatView, FileTransferConfirmationDialog, ViewerSelectionDialog - DropOverlay, ToastsView (including complex converter cases) Delete obsolete files: - Controls/IconBadge.axaml, Controls/IconBadge.axaml.cs - Themes/IconStyles.axaml (size class styles no longer needed) - Themes/BadgeStyles.axaml (text-badge inlined in AboutView) Update App.axaml: - Remove style includes for deleted theme files - Keep FluentTheme and MaterialIconStyles This creates a single, consistent API for all icon usage --- src/RemoteViewer.Client/App.axaml | 2 - .../Controls/DialogHeader.axaml | 15 +- .../Controls/DialogHeader.axaml.cs | 6 +- .../FileTransferConfirmationDialog.axaml | 16 +- .../Dialogs/ViewerSelectionDialog.axaml | 24 +-- .../Controls/DropOverlay.axaml | 10 +- src/RemoteViewer.Client/Controls/Icon.axaml | 131 +++++++++++++++ .../Controls/Icon.axaml.cs | 61 +++++++ .../Controls/IconBadge.axaml | 54 ------- .../Controls/IconBadge.axaml.cs | 57 ------- .../Controls/Toasts/ToastsView.axaml | 131 +++++++-------- .../Themes/BadgeStyles.axaml | 150 ------------------ .../Themes/IconStyles.axaml | 52 ------ .../Views/About/AboutView.axaml | 23 +-- .../Views/Chat/ChatView.axaml | 10 +- .../Views/Main/MainView.axaml | 40 ++--- .../Views/Presenter/PresenterView.axaml | 40 ++--- .../Views/Viewer/ViewerView.axaml | 96 +++++------ 18 files changed, 401 insertions(+), 517 deletions(-) create mode 100644 src/RemoteViewer.Client/Controls/Icon.axaml create mode 100644 src/RemoteViewer.Client/Controls/Icon.axaml.cs delete mode 100644 src/RemoteViewer.Client/Controls/IconBadge.axaml delete mode 100644 src/RemoteViewer.Client/Controls/IconBadge.axaml.cs delete mode 100644 src/RemoteViewer.Client/Themes/BadgeStyles.axaml delete mode 100644 src/RemoteViewer.Client/Themes/IconStyles.axaml diff --git a/src/RemoteViewer.Client/App.axaml b/src/RemoteViewer.Client/App.axaml index 9d96680..c9978ec 100644 --- a/src/RemoteViewer.Client/App.axaml +++ b/src/RemoteViewer.Client/App.axaml @@ -26,8 +26,6 @@ - - \ No newline at end of file diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml b/src/RemoteViewer.Client/Controls/DialogHeader.axaml index 37426fd..ac6563b 100644 --- a/src/RemoteViewer.Client/Controls/DialogHeader.axaml +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml @@ -8,13 +8,14 @@ - + IconProperty = AvaloniaProperty.Register(nameof(Icon), MaterialIconKind.Information); - public static readonly StyledProperty IconSizeProperty = - AvaloniaProperty.Register(nameof(IconSize), IconBadgeSize.Medium); + public static readonly StyledProperty IconSizeProperty = + AvaloniaProperty.Register(nameof(IconSize), IconSize.MD); public static readonly StyledProperty IconForegroundProperty = AvaloniaProperty.Register(nameof(IconForeground)); @@ -31,7 +31,7 @@ public MaterialIconKind Icon set => this.SetValue(IconProperty, value); } - public IconBadgeSize IconSize + public IconSize IconSize { get => this.GetValue(IconSizeProperty); set => this.SetValue(IconSizeProperty, value); diff --git a/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml index fa40777..21c971d 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/FileTransferConfirmationDialog.axaml @@ -27,12 +27,12 @@ - + - + @@ -67,7 +67,7 @@ HorizontalAlignment="Stretch" Click="OnAcceptClicked"> - + diff --git a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml index af97441..ca54bc0 100644 --- a/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml +++ b/src/RemoteViewer.Client/Controls/Dialogs/ViewerSelectionDialog.axaml @@ -30,12 +30,12 @@ - + - + @@ -88,7 +88,7 @@ HorizontalAlignment="Stretch" Click="OnCancelClicked"> - + @@ -98,7 +98,7 @@ HorizontalAlignment="Stretch" Click="OnSendClicked"> - + diff --git a/src/RemoteViewer.Client/Controls/DropOverlay.axaml b/src/RemoteViewer.Client/Controls/DropOverlay.axaml index d05d284..27d4c57 100644 --- a/src/RemoteViewer.Client/Controls/DropOverlay.axaml +++ b/src/RemoteViewer.Client/Controls/DropOverlay.axaml @@ -20,11 +20,11 @@ CornerRadius="32" Width="64" Height="64" HorizontalAlignment="Center"> - + diff --git a/src/RemoteViewer.Client/Controls/Icon.axaml b/src/RemoteViewer.Client/Controls/Icon.axaml new file mode 100644 index 0000000..38c15c5 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/Icon.axaml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/RemoteViewer.Client/Controls/Icon.axaml.cs b/src/RemoteViewer.Client/Controls/Icon.axaml.cs new file mode 100644 index 0000000..b574394 --- /dev/null +++ b/src/RemoteViewer.Client/Controls/Icon.axaml.cs @@ -0,0 +1,61 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media; +using Material.Icons; + +namespace RemoteViewer.Client.Controls; + +public enum IconSize +{ + XXS = 12, + XS = 16, + SM = 20, + MD = 24, + LG = 32, + XL = 40, + XXL = 48 +} + +public partial class Icon : UserControl +{ + public static readonly StyledProperty KindProperty = + AvaloniaProperty.Register(nameof(Kind), MaterialIconKind.Star); + + public static readonly StyledProperty SizeProperty = + AvaloniaProperty.Register(nameof(Size), IconSize.MD); + + public static readonly StyledProperty ShowAsBadgeProperty = + AvaloniaProperty.Register(nameof(ShowAsBadge), false); + + public static readonly StyledProperty BadgeBackgroundProperty = + AvaloniaProperty.Register(nameof(BadgeBackground)); + + public MaterialIconKind Kind + { + get => this.GetValue(KindProperty); + set => this.SetValue(KindProperty, value); + } + + public IconSize Size + { + get => this.GetValue(SizeProperty); + set => this.SetValue(SizeProperty, value); + } + + public bool ShowAsBadge + { + get => this.GetValue(ShowAsBadgeProperty); + set => this.SetValue(ShowAsBadgeProperty, value); + } + + public IBrush? BadgeBackground + { + get => this.GetValue(BadgeBackgroundProperty); + set => this.SetValue(BadgeBackgroundProperty, value); + } + + public Icon() + { + this.InitializeComponent(); + } +} diff --git a/src/RemoteViewer.Client/Controls/IconBadge.axaml b/src/RemoteViewer.Client/Controls/IconBadge.axaml deleted file mode 100644 index a735019..0000000 --- a/src/RemoteViewer.Client/Controls/IconBadge.axaml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/src/RemoteViewer.Client/Controls/IconBadge.axaml.cs b/src/RemoteViewer.Client/Controls/IconBadge.axaml.cs deleted file mode 100644 index 35113c5..0000000 --- a/src/RemoteViewer.Client/Controls/IconBadge.axaml.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Media; -using Material.Icons; - -namespace RemoteViewer.Client.Controls; - -public enum IconBadgeSize -{ - Small, - Medium, - Large -} - -public partial class IconBadge : UserControl -{ - public static readonly StyledProperty IconProperty = - AvaloniaProperty.Register(nameof(Icon), MaterialIconKind.Star); - - public static readonly StyledProperty SizeProperty = - AvaloniaProperty.Register(nameof(Size), IconBadgeSize.Medium); - - public static readonly StyledProperty IconForegroundProperty = - AvaloniaProperty.Register(nameof(IconForeground)); - - public static readonly StyledProperty BadgeBackgroundProperty = - AvaloniaProperty.Register(nameof(BadgeBackground)); - - public MaterialIconKind Icon - { - get => this.GetValue(IconProperty); - set => this.SetValue(IconProperty, value); - } - - public IconBadgeSize Size - { - get => this.GetValue(SizeProperty); - set => this.SetValue(SizeProperty, value); - } - - public IBrush? IconForeground - { - get => this.GetValue(IconForegroundProperty); - set => this.SetValue(IconForegroundProperty, value); - } - - public IBrush? BadgeBackground - { - get => this.GetValue(BadgeBackgroundProperty); - set => this.SetValue(BadgeBackgroundProperty, value); - } - - public IconBadge() - { - this.InitializeComponent(); - } -} diff --git a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml index 5cf2af4..a8ec400 100644 --- a/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml +++ b/src/RemoteViewer.Client/Controls/Toasts/ToastsView.axaml @@ -1,13 +1,14 @@ + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:mi="using:Material.Icons.Avalonia" + xmlns:mik="using:Material.Icons" + xmlns:toasts="using:RemoteViewer.Client.Controls.Toasts" + xmlns:ft="using:RemoteViewer.Client.Services.FileTransfer" + xmlns:conv="using:RemoteViewer.Client.Converters" + xmlns:theme="using:RemoteViewer.Client.Themes" + xmlns:controls="using:RemoteViewer.Client.Controls" + x:Class="RemoteViewer.Client.Controls.Toasts.ToastsView" + x:DataType="toasts:ToastsViewModel"> @@ -46,15 +47,15 @@ - - + Height="32" + CornerRadius="8" + Margin="{theme:Spacing Left=MD, Right=MD, Y=MD}" + VerticalAlignment="Center" + Background="{DynamicResource ToastIconBadgeBrush}"> + + @@ -64,8 +65,8 @@ - - + + @@ -75,8 +76,8 @@ - - + + @@ -109,9 +110,9 @@ - + @@ -144,18 +145,18 @@ - - - - + + + + @@ -165,8 +166,8 @@ - - + + @@ -176,8 +177,8 @@ - - + + @@ -210,10 +211,10 @@ - + - + @@ -280,11 +281,11 @@ Background="{DynamicResource ToastIconBadgeBrush}" Margin="{theme:Spacing Right=SM}" VerticalAlignment="Center"> - + - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/RemoteViewer.Client/Themes/IconStyles.axaml b/src/RemoteViewer.Client/Themes/IconStyles.axaml deleted file mode 100644 index 030b11c..0000000 --- a/src/RemoteViewer.Client/Themes/IconStyles.axaml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/RemoteViewer.Client/Views/About/AboutView.axaml b/src/RemoteViewer.Client/Views/About/AboutView.axaml index 6c00446..9c29b8a 100644 --- a/src/RemoteViewer.Client/Views/About/AboutView.axaml +++ b/src/RemoteViewer.Client/Views/About/AboutView.axaml @@ -25,10 +25,10 @@ HorizontalAlignment="Center" Margin="{theme:Spacing Top=SM, Bottom=XL}"> - + - - - + + + - + - + diff --git a/src/RemoteViewer.Client/Views/Main/MainView.axaml b/src/RemoteViewer.Client/Views/Main/MainView.axaml index 611acc5..d11d269 100644 --- a/src/RemoteViewer.Client/Views/Main/MainView.axaml +++ b/src/RemoteViewer.Client/Views/Main/MainView.axaml @@ -74,11 +74,11 @@ ToolTip.Tip="Generate new password" Classes="icon-button" VerticalAlignment="Center" - Width="32" + Width="32" Height="32"> - + @@ -89,9 +89,9 @@ Margin="{theme:Spacing Left=SM}" Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> - + @@ -142,9 +142,9 @@ ToolTip.Tip="Connect" Classes="icon-button" IsEnabled="{Binding !ConnectToDeviceCommand.IsRunning}"> - + @@ -160,8 +160,8 @@ - - + + @@ -170,8 +170,8 @@ - - + + @@ -180,8 +180,8 @@ - - + + @@ -201,11 +201,11 @@ Command="{Binding ShowAboutCommand}" ToolTip.Tip="About" Classes="icon-button" - Width="24" + Width="24" Height="24"> - + diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 8085142..257258e 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -37,8 +37,8 @@ Classes="icon-button" IsEnabled="{Binding Viewers.Count}" Command="{Binding SendFileCommand}"> - + @@ -47,8 +47,8 @@ Classes="icon-button" Command="{Binding ShowChatCommand}"> - + - + @@ -124,9 +124,9 @@ @@ -161,9 +161,9 @@ Margin="{theme:Spacing Left=SM}" Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> - + @@ -182,9 +182,9 @@ Classes="icon-button" VerticalAlignment="Center" Width="32" Height="32"> - + @@ -212,9 +212,9 @@ - + @@ -225,8 +225,8 @@ Command="{Binding ToggleInputBlockCommand}" ToolTip.Tip="{Binding IsInputBlocked, Converter={conv:BoolToObject TrueValue='Unblock input', FalseValue='Block input'}}" Classes="icon-button"> - + diff --git a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml index ad651e8..aec00fb 100644 --- a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml +++ b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml @@ -69,8 +69,8 @@ @@ -90,8 +90,8 @@ - + @@ -104,8 +104,8 @@ - + @@ -119,16 +119,16 @@ - + @@ -152,9 +152,9 @@ CornerRadius="4" Background="{Binding IsPresenter, Converter={x:Static conv:BoolToAccentBrushConverter.Instance}}"> - + @@ -164,8 +164,8 @@ - + @@ -174,8 +174,8 @@ Classes="icon-button" Command="{Binding ShowChatCommand}"> - + - + @@ -248,13 +248,13 @@ Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Left)'}"> - - - + + + @@ -281,13 +281,13 @@ Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Right)'}"> - - - + + + @@ -314,13 +314,13 @@ Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Up)'}"> - - - + + + @@ -347,13 +347,13 @@ Opacity="0.6" ToolTip.Tip="{Binding FriendlyName, StringFormat='Navigate to {0} (Ctrl+Down)'}"> - - - + + + From 96bc73b6457987047bca34ec94e1d6fa6fda1d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Tue, 13 Jan 2026 16:45:24 +0100 Subject: [PATCH 10/14] Complete migration from MaterialIcon to unified Icon control - Fix missing closing brace in ViewerView.axaml binding expression - Fix DialogHeader.axaml to use Kind property instead of Icon - Migrate DisplayMiniMap.axaml.cs to use custom Icon control - Replace all mi:MaterialIcon usages with controls:Icon in ToastsView and PresenterView - Remove unused Material.Icons.Avalonia xmlns declarations Co-Authored-By: Claude Opus 4.5 --- .../Controls/DialogHeader.axaml | 2 +- .../Controls/DisplayMiniMap.axaml.cs | 10 +- .../Controls/Toasts/ToastsView.axaml | 95 +++++++++---------- .../Views/Presenter/PresenterView.axaml | 9 +- .../Views/Viewer/ViewerView.axaml | 2 +- 5 files changed, 57 insertions(+), 61 deletions(-) diff --git a/src/RemoteViewer.Client/Controls/DialogHeader.axaml b/src/RemoteViewer.Client/Controls/DialogHeader.axaml index ac6563b..0e6a5fe 100644 --- a/src/RemoteViewer.Client/Controls/DialogHeader.axaml +++ b/src/RemoteViewer.Client/Controls/DialogHeader.axaml @@ -9,7 +9,7 @@ @@ -47,14 +46,14 @@ + Height="32" + CornerRadius="8" + Margin="{theme:Spacing Left=MD, Right=MD, Y=MD}" + VerticalAlignment="Center" + Background="{DynamicResource ToastIconBadgeBrush}"> + HorizontalAlignment="Center" + VerticalAlignment="Center"> @@ -105,14 +104,14 @@ - - + + + Size="XS" + Foreground="{DynamicResource ToastForegroundBrush}"/> @@ -145,17 +144,17 @@ - - + + + HorizontalAlignment="Center" + VerticalAlignment="Center"> @@ -240,14 +239,14 @@ - - + + + Size="XS" + Foreground="{DynamicResource ToastForegroundBrush}"/> @@ -282,10 +281,10 @@ Margin="{theme:Spacing Right=SM}" VerticalAlignment="Center"> + Size="XS" + HorizontalAlignment="Center" + VerticalAlignment="Center" + Foreground="{StaticResource ToastTransferAccentBrush}"/> - - + + + + - - + Size="XS" + Foreground="{DynamicResource ToastForegroundBrush}"/> + + @@ -213,7 +212,7 @@ Orientation="Horizontal" Spacing="{theme:Spacing SM}"> - + diff --git a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml index aec00fb..a71db7e 100644 --- a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml +++ b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml @@ -152,7 +152,7 @@ CornerRadius="4" Background="{Binding IsPresenter, Converter={x:Static conv:BoolToAccentBrushConverter.Instance}}"> - From 9e70b4c16d2764d98d4ced555b47d32fc8a7bd65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Tue, 13 Jan 2026 21:50:07 +0100 Subject: [PATCH 11/14] Remove 18 unused color resources from Colors.axaml Removed unused semantic state variants (SuccessHover/Pressed, ErrorHover/Pressed), window chrome colors, SurfaceBrush, TextDisabledBrush, BorderDefault/Strong, CardElevatedBackgroundBrush, InfoBrush, and WarningBackground/Foreground. Reduces resource count from 53 to 35 (34% reduction). Co-Authored-By: Claude Opus 4.5 --- src/RemoteViewer.Client/Themes/Colors.axaml | 29 +-------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/src/RemoteViewer.Client/Themes/Colors.axaml b/src/RemoteViewer.Client/Themes/Colors.axaml index aefee56..b71531d 100644 --- a/src/RemoteViewer.Client/Themes/Colors.axaml +++ b/src/RemoteViewer.Client/Themes/Colors.axaml @@ -12,11 +12,7 @@ - - - - @@ -24,14 +20,9 @@ - + - - - - - @@ -55,15 +46,10 @@ - - - - - @@ -71,12 +57,9 @@ - - - @@ -95,7 +78,6 @@ - @@ -104,15 +86,10 @@ - - - - - @@ -120,12 +97,9 @@ - - - @@ -144,7 +118,6 @@ - From ad7f2d4b2aabb3e250e9af6bb4b19c129d649306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fele?= Date: Tue, 13 Jan 2026 22:38:59 +0100 Subject: [PATCH 12/14] Replace custom styles and colors with Avalonia built-in resources - Replace action-primary/action-secondary with Avalonia accent class - Replace AccentBrush with AccentButtonBackground - Replace text brushes with SystemControlDisabledBaseMediumLowBrush - Replace BorderSubtleBrush with SystemControlForegroundBaseLowBrush - Remove redundant icon foregrounds (inherit from parent) - Simplify ButtonStyles.axaml to base CornerRadius and icon-button only Co-Authored-By: Claude Opus 4.5 --- CLAUDE.md | 16 +++-- .../FileTransferConfirmationDialog.axaml | 5 +- .../Dialogs/ViewerSelectionDialog.axaml | 7 +- .../Controls/DisplayMiniMap.axaml | 2 +- .../Controls/DropOverlay.axaml | 2 +- src/RemoteViewer.Client/Controls/Icon.axaml | 2 +- .../Controls/Toasts/ToastsView.axaml | 4 +- .../Themes/ButtonStyles.axaml | 65 ++----------------- src/RemoteViewer.Client/Themes/Colors.axaml | 50 -------------- .../Themes/Typography.axaml | 10 +-- .../Views/About/AboutView.axaml | 7 +- .../Views/Chat/ChatView.axaml | 4 +- .../Views/Main/MainView.axaml | 11 ++-- .../Views/Presenter/PresenterView.axaml | 8 +-- .../Views/Viewer/ViewerView.axaml | 4 +- 15 files changed, 41 insertions(+), 156 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 30cb1b2..00576e2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,10 +114,10 @@ CornerRadius="{StaticResource CornerRadiusCard}" ``` ### Colors (use DynamicResource for theme support) -- **Text**: `TextPrimaryBrush`, `TextSecondaryBrush`, `TextMutedBrush`, `TextDisabledBrush` -- **Surfaces**: `SurfaceBrush`, `SurfaceElevatedBrush`, `CardBackgroundBrush` -- **Borders**: `BorderSubtleBrush`, `BorderDefaultBrush`, `BorderStrongBrush` -- **Semantic**: `AccentBrush`, `SuccessBrush`, `ErrorBrush`, `WarningBrush`, `InfoBrush` +- **Muted text**: `SystemControlDisabledBaseMediumLowBrush` (Avalonia built-in) +- **Accent**: `AccentButtonBackground`, `AccentButtonForeground` (Avalonia built-in) +- **Surfaces**: `SurfaceElevatedBrush`, `SurfaceOverlayBrush`, `CardBackgroundBrush` +- **Semantic**: `SuccessBrush`, `ErrorBrush`, `WarningBrush` ## Typography Classes @@ -130,10 +130,12 @@ Apply via `Classes="class-name"` on TextBlock: ## Button Styles +All buttons automatically get `CornerRadius="6"` from the base style. + ```xml - diff --git a/src/RemoteViewer.Client/Themes/ButtonStyles.axaml b/src/RemoteViewer.Client/Themes/ButtonStyles.axaml index 5643721..301eb0c 100644 --- a/src/RemoteViewer.Client/Themes/ButtonStyles.axaml +++ b/src/RemoteViewer.Client/Themes/ButtonStyles.axaml @@ -5,6 +5,11 @@ + + + - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/RemoteViewer.Client/Themes/Colors.axaml b/src/RemoteViewer.Client/Themes/Colors.axaml index b71531d..9855195 100644 --- a/src/RemoteViewer.Client/Themes/Colors.axaml +++ b/src/RemoteViewer.Client/Themes/Colors.axaml @@ -1,27 +1,11 @@ - - - - - - - - - - - - - - - - @@ -45,22 +29,10 @@ - - - - - - - - - - - - @@ -71,11 +43,6 @@ - - - - - @@ -85,22 +52,10 @@ - - - - - - - - - - - - @@ -111,11 +66,6 @@ - - - - - diff --git a/src/RemoteViewer.Client/Themes/Typography.axaml b/src/RemoteViewer.Client/Themes/Typography.axaml index 4198fc9..7d7bfc6 100644 --- a/src/RemoteViewer.Client/Themes/Typography.axaml +++ b/src/RemoteViewer.Client/Themes/Typography.axaml @@ -14,7 +14,6 @@ - @@ -22,7 +21,6 @@ - @@ -30,7 +28,6 @@ - @@ -38,7 +35,7 @@ - + @@ -46,7 +43,7 @@ - + @@ -54,12 +51,11 @@ - diff --git a/src/RemoteViewer.Client/Views/About/AboutView.axaml b/src/RemoteViewer.Client/Views/About/AboutView.axaml index 9c29b8a..1357374 100644 --- a/src/RemoteViewer.Client/Views/About/AboutView.axaml +++ b/src/RemoteViewer.Client/Views/About/AboutView.axaml @@ -28,7 +28,7 @@ + BadgeBackground="{DynamicResource AccentButtonBackground}"/> + Foreground="{DynamicResource SystemControlDisabledBaseMediumLowBrush}"/> @@ -81,7 +81,7 @@ + Foreground="{DynamicResource SystemControlDisabledBaseMediumLowBrush}"/> diff --git a/src/RemoteViewer.Client/Views/Main/MainView.axaml b/src/RemoteViewer.Client/Views/Main/MainView.axaml index d11d269..df6588b 100644 --- a/src/RemoteViewer.Client/Views/Main/MainView.axaml +++ b/src/RemoteViewer.Client/Views/Main/MainView.axaml @@ -77,8 +77,7 @@ Width="32" Height="32"> + Size="XXS"/> @@ -90,8 +89,7 @@ Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> + Size="SM"/> @@ -144,7 +142,7 @@ IsEnabled="{Binding !ConnectToDeviceCommand.IsRunning}"> + Foreground="{DynamicResource AccentButtonBackground}"/> @@ -204,8 +202,7 @@ Width="24" Height="24"> + Size="XXS"/> diff --git a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml index 41977da..b62061c 100644 --- a/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml +++ b/src/RemoteViewer.Client/Views/Presenter/PresenterView.axaml @@ -116,7 +116,7 @@ @@ -161,8 +161,7 @@ Command="{Binding CopyCredentialsCommand}" ToolTip.Tip="Copy ID and password"> + Size="SM"/> @@ -182,8 +181,7 @@ VerticalAlignment="Center" Width="32" Height="32"> + Size="XS"/> diff --git a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml index a71db7e..0377a31 100644 --- a/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml +++ b/src/RemoteViewer.Client/Views/Viewer/ViewerView.axaml @@ -134,7 +134,7 @@ + Background="{DynamicResource SystemControlForegroundBaseLowBrush}"/>