diff --git a/README.md b/README.md
index a4b920d..50852ee 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ Core functionality is under an open source license to help increase the adoption
### Project Documentation
- **[Developer Guidelines](docs/CLAUDE.md)** - Development guidelines and build commands
+- **[UI Style Guide](src/UI/Windows/Styles/StyleGuide.md)** - Comprehensive design system and styling guidelines
### Architecture Plans
- **[Connection Plugin Architecture](docs/CONNECTION_PLUGIN_ARCHITECTURE.md)** - Plan for implementing pluggable connection types (Serial, Bluetooth, Network)
diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md
index 0621c51..b66db58 100644
--- a/docs/CLAUDE.md
+++ b/docs/CLAUDE.md
@@ -16,14 +16,24 @@
## Code Style Guidelines
- Use C# 8.0+ features with async/await patterns for asynchronous operations
-- Follow MVVM design pattern for view models with ObservableObject and RelayCommand
+- Follow the MVVM design pattern for view models with ObservableObject and RelayCommand
- Use dependency injection for services
- Include XML documentation for public interfaces and methods
- Use PascalCase for class, method, and public property names
-- Use _camelCase for private fields with underscore prefix
+- Use _camelCase for private fields with an underscore prefix
- Implement defensive programming with null checks for constructor parameters
- Use standard exception handling with try/catch blocks around external operations
- Prefer async/await over direct Task management
- Organize files into clear namespaces (Core, Models, Services, ViewModels, etc.)
- Use meaningful variable names that reflect their purpose
- Keep methods focused and small with a single responsibility
+
+## UI Style Guidelines
+- **Always use standard styles** - Apply predefined styles from the design system instead of inline properties
+- **Use design tokens for spacing** - Reference `{StaticResource Margin.Card}` instead of hardcoding values
+- **Apply semantic colors** - Use `{StaticResource Brush.Error}` instead of hardcoded colors like "Red"
+- **Follow the style hierarchy** - Check ComponentStyles.xaml and LayoutTemplates.xaml before creating custom styles
+- **Update existing code** - When modifying files, replace inline styling with standard styles
+- **Create reusable patterns** - If you find yourself repeating XAML structures, consider adding a new style or template
+
+For detailed UI styling guidelines and examples, see: `src/UI/Windows/Styles/StyleGuide.md`
diff --git a/src/Core/Resources/Resources.resx b/src/Core/Resources/Resources.resx
index 8a32b24..38473df 100644
--- a/src/Core/Resources/Resources.resx
+++ b/src/Core/Resources/Resources.resx
@@ -219,6 +219,14 @@
Baud Rate
Label for baud rate selection
+
+ Connection Settings
+ Header for connection settings section
+
+
+ Security Settings
+ Header for security settings section
+
Address
Label for device address input
@@ -257,6 +265,30 @@
Device Action
Header for device action section
+
+ LED Color
+ Label for LED color selection
+
+
+ Selected File
+ Label for file selection in file transfer
+
+
+ Browse
+ Button text for file browser
+
+
+ Progress
+ Label for file transfer progress
+
+
+ Bytes/
+ Text between transferred and total bytes
+
+
+ Bytes
+ Text for bytes unit
+
diff --git a/src/Core/ViewModels/Pages/ConnectViewModel.cs b/src/Core/ViewModels/Pages/ConnectViewModel.cs
index 3e6cbf6..1ee8cdb 100644
--- a/src/Core/ViewModels/Pages/ConnectViewModel.cs
+++ b/src/Core/ViewModels/Pages/ConnectViewModel.cs
@@ -63,13 +63,12 @@ public ConnectViewModel(IDialogService dialogService, IDeviceManagementService d
private void OnDeviceManagementServiceOnTraceEntryReceived(object? sender, TraceEntry traceEntry)
{
- if (_deviceManagementService.IsUsingSecureChannel) return;
-
+ // Update activity indicators based on raw trace entry direction (works for encrypted packets too)
+ UpdateActivityIndicators(traceEntry.Direction);
+
PacketTraceEntry? packetTraceEntry = BuildPacketTraceEntry(traceEntry);
if (packetTraceEntry == null) return;
- UpdateActivityIndicators(packetTraceEntry.Direction);
-
_lastPacketEntry = packetTraceEntry;
}
diff --git a/src/Core/ViewModels/Pages/ManageViewModel.cs b/src/Core/ViewModels/Pages/ManageViewModel.cs
index d022e49..3148582 100644
--- a/src/Core/ViewModels/Pages/ManageViewModel.cs
+++ b/src/Core/ViewModels/Pages/ManageViewModel.cs
@@ -196,7 +196,17 @@ private void DeviceManagementServiceOnKeypadReadReceived(object? sender, string
private void OnDeviceManagementServiceOnTraceEntryReceived(object? sender, TraceEntry traceEntry)
{
- if (_deviceManagementService.IsUsingSecureChannel) return;
+ // Update activity indicators based on raw trace entry direction (works for encrypted packets too)
+ switch (traceEntry.Direction)
+ {
+ // Flash the appropriate LED based on a direction
+ case TraceDirection.Output:
+ LastTxActiveTime = DateTime.Now;
+ break;
+ case TraceDirection.Input or TraceDirection.Trace:
+ LastRxActiveTime = DateTime.Now;
+ break;
+ }
var build = new PacketTraceEntryBuilder();
PacketTraceEntry packetTraceEntry;
@@ -208,17 +218,6 @@ private void OnDeviceManagementServiceOnTraceEntryReceived(object? sender, Trace
{
return;
}
-
- switch (packetTraceEntry.Direction)
- {
- // Flash the appropriate LED based on a direction
- case TraceDirection.Output:
- LastTxActiveTime = DateTime.Now;
- break;
- case TraceDirection.Input or TraceDirection.Trace:
- LastRxActiveTime = DateTime.Now;
- break;
- }
_lastPacketEntry = packetTraceEntry;
}
diff --git a/src/Core/ViewModels/Pages/MonitorViewModel.cs b/src/Core/ViewModels/Pages/MonitorViewModel.cs
index 6d60cfc..8b08c18 100644
--- a/src/Core/ViewModels/Pages/MonitorViewModel.cs
+++ b/src/Core/ViewModels/Pages/MonitorViewModel.cs
@@ -53,8 +53,18 @@ private void InitializePollingMetrics()
private void OnDeviceManagementServiceOnTraceEntryReceived(object? _, TraceEntry traceEntry)
{
UsingSecureChannel = _deviceManagementService.IsUsingSecureChannel;
-
- if (UsingSecureChannel) return;
+
+ // Update activity indicators based on raw trace entry direction (works for encrypted packets too)
+ switch (traceEntry.Direction)
+ {
+ // Flash appropriate LED based on direction
+ case Output:
+ LastTxActiveTime = DateTime.Now;
+ break;
+ case Input or Trace:
+ LastRxActiveTime = DateTime.Now;
+ break;
+ }
var build = new PacketTraceEntryBuilder();
PacketTraceEntry packetTraceEntry;
@@ -67,17 +77,6 @@ private void OnDeviceManagementServiceOnTraceEntryReceived(object? _, TraceEntry
return;
}
- switch (packetTraceEntry.Direction)
- {
- // Flash appropriate LED based on direction
- case Output:
- LastTxActiveTime = DateTime.Now;
- break;
- case Input or Trace:
- LastRxActiveTime = DateTime.Now;
- break;
- }
-
bool notDisplaying = packetTraceEntry.Packet.CommandType == CommandType.Poll ||
_lastPacketEntry?.Packet.CommandType == CommandType.Poll &&
packetTraceEntry.Packet.ReplyType == ReplyType.Ack;
diff --git a/src/UI/Windows/App.xaml b/src/UI/Windows/App.xaml
index 398e842..68df2ac 100644
--- a/src/UI/Windows/App.xaml
+++ b/src/UI/Windows/App.xaml
@@ -11,20 +11,23 @@
+
+
+
+
-
+
+
+
+
+
+
-
+
+
+
+
diff --git a/src/UI/Windows/Helpers/CopyTextBoxHelper.cs b/src/UI/Windows/Helpers/CopyTextBoxHelper.cs
new file mode 100644
index 0000000..79920ee
--- /dev/null
+++ b/src/UI/Windows/Helpers/CopyTextBoxHelper.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+
+namespace OSDPBench.Windows.Helpers;
+
+public static class CopyTextBoxHelper
+{
+ public static readonly DependencyProperty CopyCommandProperty =
+ DependencyProperty.RegisterAttached(
+ "CopyCommand",
+ typeof(ICommand),
+ typeof(CopyTextBoxHelper),
+ new PropertyMetadata(null));
+
+ public static ICommand GetCopyCommand(DependencyObject obj)
+ {
+ return (ICommand)obj.GetValue(CopyCommandProperty);
+ }
+
+ public static void SetCopyCommand(DependencyObject obj, ICommand value)
+ {
+ obj.SetValue(CopyCommandProperty, value);
+ }
+
+ public static readonly ICommand DefaultCopyCommand = new RelayCommand(
+ parameter =>
+ {
+ if (parameter is TextBox textBox && !string.IsNullOrEmpty(textBox.Text))
+ {
+ try
+ {
+ Clipboard.SetText(textBox.Text);
+ }
+ catch (System.Runtime.InteropServices.COMException)
+ {
+ // Clipboard is locked by another application, ignore silently
+ // This is a common Windows issue and shouldn't crash the app
+ }
+ }
+ });
+}
+
+public class RelayCommand : ICommand
+{
+ private readonly Action