Skip to content

Conversation

@shuaiyuanxx
Copy link
Contributor

@shuaiyuanxx shuaiyuanxx commented Dec 23, 2025

Summary of the Pull Request

PR Checklist

  • Closes: #xxx
  • Communication: I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected
  • Tests: Added/updated and all pass
  • Localization: All end-user-facing strings can be localized
  • Dev docs: Added/updated
  • New binaries: Added on the required places
  • Documentation updated: If checked, please file a pull request on our docs repo and link it here: #xxx

Detailed Description of the Pull Request / Additional comments

Validation Steps Performed

@diffray-bot
Copy link

Changes Summary

This PR extends the Advanced Paste module to support text-to-image generation capabilities alongside the existing chat completion functionality. It introduces a new PasteAIUsage enum to differentiate between ChatCompletion and TextToImage operations, adds UI components to display image results, and implements the necessary backend logic to handle both modalities through the Semantic Kernel framework.

Type: feature

Components Affected: AdvancedPaste Module - Core Service, SemanticKernelPasteProvider - AI Provider Implementation, OptionsViewModel - UI Logic, Settings UI - Advanced Paste Configuration, XAML UI Components - PromptBox and AdvancedPastePage, Settings Library - Provider Definition

Files Changed
File Summary Change Impact
...settings-ui/Settings.UI.Library/PasteAIUsage.cs New enum to represent AI usage types: ChatCompletion and TextToImage 🟡
...i/Settings.UI.Library/PasteAIUsageExtensions.cs Extensions for converting PasteAIUsage enum to/from configuration strings 🟡
...ettings.UI.Library/PasteAIProviderDefinition.cs Added Usage, ImageWidth, and ImageHeight properties with JSON serialization support ✏️ 🟡
...edPaste/Services/CustomActions/PasteAIConfig.cs Added Usage, ImageWidth, and ImageHeight properties to configuration model ✏️ 🟡
...es/CustomActions/SemanticKernelPasteProvider.cs Major refactoring to support text-to-image generation; split chat completion logic into separate method; added ProcessTextToImageAsync method with OpenAI and Azure OpenAI support ✏️ 🔴
...s/CustomActions/CustomActionTransformService.cs Updated to map provider Usage, ImageWidth, and ImageHeight to config ✏️ 🟢
...ste/AdvancedPaste/Helpers/DataPackageHelpers.cs Added CreateFromImage method to support copying images to clipboard ✏️ 🟢
...Paste/AdvancedPasteXAML/Controls/PromptBox.xaml Enhanced to display both text and image results; added Image control with data URI support; refactored badge section to display usage type ✏️ 🔴
...te/AdvancedPasteXAML/Controls/PromptBox.xaml.cs Simplified flyout handling and removed redundant SyncProviderSelection call ✏️ 🟢
...AML/Converters/PasteAIUsageToStringConverter.cs New converter to transform PasteAIUsage enum values to localized display strings 🟢
...te/AdvancedPaste/ViewModels/OptionsViewModel.cs Added image result handling with HasCustomFormatImage, HasCustomFormatText, and CustomFormatImageResult properties; implemented base64 data URI parsing and image display ✏️ 🔴
....UI/Converters/PasteAIUsageToStringConverter.cs New converter in Settings UI for localizing PasteAIUsage enum values 🟢
...gs.UI/SettingsXAML/Views/AdvancedPastePage.xaml Extended settings page UI with new bindings and controls for AI usage configuration ✏️ 🟡
...UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs Added logic to handle AI usage selection and related UI state management ✏️ 🟡
...ettings.UI/ViewModels/AdvancedPasteViewModel.cs Updated to support AI usage configuration in the settings view model ✏️ 🟡
...aste/AdvancedPaste/Strings/en-us/Resources.resw Added localization strings for ChatCompletion and TextToImage usage labels ✏️ 🟢
...ngs-ui/Settings.UI/Strings/en-us/Resources.resw Added localization strings for AI usage type labels in settings UI ✏️ 🟢
Architecture Impact
  • New Patterns: Enum-based feature toggle pattern for AI modalities, Converter pattern for value transformation in UI, Data URI handling pattern for image serialization
  • Dependencies: added: System.Net.Http (for downloading images in TextToImage flow), added: Microsoft.SemanticKernel.TextToImage namespace
  • Coupling: Increased coupling between UI layer and AI provider configuration through new enum and usage properties. Settings UI now tightly coupled to PasteAIUsage enum values.
  • Breaking Changes: PasteAIProviderDefinition now includes Usage, ImageWidth, ImageHeight fields - existing provider configs without these fields will receive default values, SemanticKernelPasteProvider refactored to separate chat and image processing - any code directly calling internal methods may be affected

Risk Areas: Image data URI parsing: Uses simple Split(',')[1] which could fail if data URI format is unexpected - no validation of base64 format, Memory management: MemoryStream created in CustomFormatImageResult getter without explicit disposal using 'using' statement - potential resource leak on repeated image conversions, Network operations: HttpClient in ProcessTextToImageAsync is created fresh each call - should use shared HttpClient or factory pattern, Exception handling: Broad catch-all exceptions in image parsing/creation could mask underlying issues; errors are logged but silently fail to user, Settings persistence: New provider fields (Usage, ImageWidth, ImageHeight) must be properly serialized/deserialized - no validation shown for image dimensions, Semantic Kernel versioning: Uses experimental APIs (#pragma warning SKEXP0001, SKEXP0010) which may change in future versions

Suggestions
  • Add validation for image dimensions (ImageWidth, ImageHeight) - ensure they are positive and within reasonable bounds
  • Refactor image data URI parsing to a dedicated utility method with proper validation of base64 format
  • Use 'using' statement or dependency injection for HttpClient to avoid resource leaks
  • Consider implementing IDisposable for MemoryStream or using 'using' statement in CustomFormatImageResult getter
  • Add unit tests for text-to-image flow, data URI parsing, and image clipboard operations
  • Add error handling feedback to user when image generation fails or image parsing fails
  • Document the experimental API dependencies and plan for Semantic Kernel version updates
  • Consider extracting image conversion logic into a separate service class for better testability and reusability
  • Verify localization coverage for all new user-facing strings in both Settings.UI and AdvancedPaste modules

Full review in progress... | Powered by diffray

Comment on lines +635 to +640
var base64Data = CustomFormatResult.Split(',')[1];
var bytes = Convert.FromBase64String(base64Data);
var stream = new System.IO.MemoryStream(bytes);
var image = new BitmapImage();
image.SetSource(stream.AsRandomAccessStream());
return image;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - Split operation on string without bounds check
Agent: csharp

Category: bug

Description:
CustomFormatResult.Split(',')[1] is accessed without verifying the split produces at least 2 elements. If the data URI is malformed (missing comma), this will throw IndexOutOfRangeException.

Suggestion:
Use TrySplit or check array length before accessing index 1: var parts = CustomFormatResult.Split(','); if (parts.Length > 1) { var base64Data = parts[1]; }

Confidence: 85%
Rule: bug_collection_access_csharp
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +663 to +668
var base64Data = text.Split(',')[1];
var bytes = Convert.FromBase64String(base64Data);
var stream = new System.IO.MemoryStream(bytes);
var dataPackage = DataPackageHelpers.CreateFromImage(Windows.Storage.Streams.RandomAccessStreamReference.CreateFromStream(stream.AsRandomAccessStream()));
await CopyPasteAndHideAsync(dataPackage);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 HIGH - Split operation on string without bounds check
Agent: csharp

Category: bug

Description:
text.Split(',')[1] is accessed in PasteCustomAsync without verifying the split produces at least 2 elements. If a data URI is malformed, this will throw IndexOutOfRangeException.

Suggestion:
Validate the split result: var parts = text.Split(','); if (parts.Length > 1) { var base64Data = parts[1]; } else { Logger.LogError(...); return; }

Confidence: 85%
Rule: bug_collection_access_csharp
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

{
OnPropertyChanged(nameof(DisplayName));
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Implicit default value in setter
Agent: csharp

Category: quality

Description:
ServiceType property setter silently changes null/whitespace to 'OpenAI' without documentation. This behavior should be documented.

Suggestion:
Add XML documentation to explain the default 'OpenAI' behavior, or consider making the default behavior more explicit.

Confidence: 60%
Rule: qual_misleading_names_csharp
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +7 to +28
public static class PasteAIUsageExtensions
{
public static string ToConfigString(this PasteAIUsage usage)
{
return usage switch
{
PasteAIUsage.ChatCompletion => "ChatCompletion",
PasteAIUsage.TextToImage => "TextToImage",
_ => "ChatCompletion",
};
}

public static PasteAIUsage FromConfigString(string usage)
{
return usage switch
{
"TextToImage" => PasteAIUsage.TextToImage,
_ => PasteAIUsage.ChatCompletion,
};
}
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Missing XML documentation for public class and methods
Agent: csharp

Category: docs

Description:
The public static class PasteAIUsageExtensions and its public methods ToConfigString() and FromConfigString() lack XML documentation.

Suggestion:
Add

, , and tags to the class and all public methods.

Why this matters: Enables IntelliSense and consistent API usage across assemblies.

Confidence: 75%
Rule: cs_xml_documentation_for_public_apis
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +7 to +12
public enum PasteAIUsage
{
ChatCompletion,
TextToImage,
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Missing XML documentation for public enum
Agent: csharp

Category: docs

Description:
The public enum PasteAIUsage lacks XML documentation. Enum members ChatCompletion and TextToImage are undocumented.

Suggestion:
Add

tag to the enum and tags to each enum member.

Why this matters: Enables IntelliSense and consistent API usage across assemblies.

Confidence: 70%
Rule: cs_xml_documentation_for_public_apis
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +103 to +104
var width = _config.ImageWidth > 0 ? _config.ImageWidth : 1024;
var height = _config.ImageHeight > 0 ? _config.ImageHeight : 1024;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Magic number 1024 for image dimensions
Agent: csharp

Category: quality

Description:
Magic numbers 1024 used for default image width and height. Should be extracted to named constants for clarity and maintainability.

Suggestion:
Extract to constants: private const int DefaultImageWidth = 1024; private const int DefaultImageHeight = 1024;

Why this matters: Magic numbers obscure intent and make changes error-prone when the same value is used in multiple places.

Confidence: 75%
Rule: qual_magic_numbers_csharp
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +313 to +316
return (serviceType.Equals("OpenAI", StringComparison.OrdinalIgnoreCase) ||
serviceType.Equals("AzureOpenAI", StringComparison.OrdinalIgnoreCase))
? Visibility.Visible
: Visibility.Collapsed;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW - Magic string comparisons for service types
Agent: csharp

Category: quality

Description:
String literals 'OpenAI' and 'AzureOpenAI' used in string comparisons instead of using the AIServiceType enum directly.

Suggestion:
Use AIServiceType enum comparison via ServiceTypeKind property or convert using ToAIServiceType() method first.

Why this matters: Using enums improves type safety and enables compile-time checking.

Confidence: 65%
Rule: cs_use_enums_instead_of_magic_strings
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

}
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM - Magic number 30 for cleanup delay
Agent: csharp

Category: quality

Description:
Magic number 30 used for temporary file cleanup delay timeout.

Suggestion:
Extract to constant: private const int CleanupDelaySeconds = 30;

Why this matters: Magic numbers obscure intent and make changes error-prone.

Confidence: 70%
Rule: qual_magic_numbers_csharp
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Comment on lines +31 to +32
private int _imageWidth = 1024;
private int _imageHeight = 1024;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW - Magic numbers for image dimensions
Agent: csharp

Category: quality

Description:
Hard-coded 1024 values for default image width and height. Should be extracted to constants.

Suggestion:
Extract to constants for reusability and maintainability

Why this matters: Magic numbers obscure intent and make changes error-prone.

Confidence: 70%
Rule: qual_magic_numbers_csharp
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

public class PasteAIProviderDefinition : INotifyPropertyChanged
{
private string _id = Guid.NewGuid().ToString("N");
private string _serviceType = "OpenAI";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 LOW - Magic string 'OpenAI' as default service type
Agent: csharp

Category: quality

Description:
String literal 'OpenAI' used as default service type in field initialization. This duplicates the default found in AdvancedPasteViewModel.

Suggestion:
Extract to a constant or use AIServiceType enum for consistency

Why this matters: Improves type safety and consistency.

Confidence: 62%
Rule: cs_use_enums_instead_of_magic_strings
Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

@diffray-bot
Copy link

Review Summary

Free public review - Want AI code reviews on your PRs? Check out diffray.ai

Validated 64 issues: 30 kept, 34 filtered

Issues Found: 30

💬 See 12 individual line comment(s) for details.

📊 13 unique issue type(s) across 30 location(s)

📋 Full issue list (click to expand)

🟠 HIGH - Split operation on string without bounds check (2 occurrences)

Agent: csharp

Category: bug

📍 View all locations
File Description Suggestion Confidence
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:635-640 CustomFormatResult.Split(',')[1] is accessed without verifying the split produces at least 2 element... Use TrySplit or check array length before accessing index 1: var parts = CustomFormatResult.Split(',... 85%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:663-668 text.Split(',')[1] is accessed in PasteCustomAsync without verifying the split produces at least 2 e... Validate the split result: var parts = text.Split(','); if (parts.Length > 1) { var base64Data = par... 85%

Rule: bug_collection_access_csharp


🟡 MEDIUM - Unsafe registry value casting without type validation (2 occurrences)

Agent: security

Category: security

Why this matters: Avoids exceptions as control flow and handles invalid input gracefully.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:346-347 Registry.GetValue returns object type and is directly cast to int without validation. Default value ... Use safe casting pattern: `var value = Registry.GetValue(registryKey, "EnableClipboardHistory", 0); ... 85%
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:359-364 Registry.GetValue result is directly cast to int without type checking. Null check exists but no val... Use safe casting: `if (allowClipboardHistory is int intValue) { return intValue == 0; } return false... 78%

Rule: cs_use_tryparse_for_string_conversions


🟡 MEDIUM - Magic strings duplicated across multiple methods (2 occurrences)

Agent: quality

Category: quality

Why this matters: Makes code harder to change safely.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:582-722 Status text strings appear in multiple locations without being extracted to constants. Same status m... Extract to private const fields: private const string StatusTextNoModelsDetected = "No local models ... 80%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:548-642 Multiple methods repeat identical pattern: null-check FoundryLocalPicker, then update multiple prope... Extract a helper method: `private void UpdateFoundryPickerState(bool isLoading, bool isAvailable, st... 75%

Rule: quality_inconsistent_naming


🟡 MEDIUM - Missing XML documentation for public class and methods (4 occurrences)

Agent: csharp

Category: docs

Why this matters: Enables IntelliSense and consistent API usage across assemblies.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI.Library/PasteAIUsageExtensions.cs:7-28 The public static class PasteAIUsageExtensions and its public methods ToConfigString() and FromConfi... Add , , and tags to the class and all public methods. 75%
src/settings-ui/Settings.UI.Library/PasteAIUsage.cs:7-12 The public enum PasteAIUsage lacks XML documentation. Enum members ChatCompletion and TextToImage ar... Add tag to the enum and tags to each enum member. 70%
src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/PasteAIConfig.cs:28-38 The public class PasteAIConfig lacks any XML documentation. The class has 12 public auto-properties ... Add tag to the class and tags to each public property. 80%
src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs:23-41 The public sealed class CustomActionTransformService and its public constructor lack XML documentati... Add tag to the class and and tags to the constructor. 65%

Rule: cs_xml_documentation_for_public_apis


🟡 MEDIUM - Event handler subscription without unsubscription

Agent: csharp

Category: quality

Why this matters: This pattern commonly causes memory leaks in UI components.

File: src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml.cs:63-70

Description: ViewModel.PropertyChanged and ViewModel.PreviewRequested events are subscribed in constructor but may not be unsubscribed if the UserControl is reused or disposed.

Suggestion: Implement IDisposable or override OnUnloaded to unsubscribe from ViewModel.PropertyChanged and ViewModel.PreviewRequested events.

Confidence: 65%

Rule: csharp_dispose_not_called


🟡 MEDIUM - Empty catch block in Dispose method (4 occurrences)

Agent: csharp

Category: quality

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:1048-1051 Catch block in the Dispose method swallows exceptions from cancellation without any handling or logg... Add a debug-level log: catch (Exception ex) { Logger.LogDebug("Cancellation failed during disposal",... 65%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:299-301 Catch block catches exceptions without logging or handling them for file deletion operation. Comment... Add a comment explaining why exceptions are being ignored, or consider logging at debug level with c... 62%
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:814 Multiple catch blocks throughout the file swallow exceptions without logging (lines 308, 321, 349, 3... Add meaningful error logging or at minimum a code comment explaining the intent: catch (Exception ex... 70%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:144-147 Code has a comment explaining intent but no logging mechanism for debugging purposes. Add meaningful logging or explanation: catch (Exception ex) { Logger.LogDebug("Failed to refresh bin... 60%

Rule: cs_avoid_empty_catch_blocks


🟡 MEDIUM - Potential null reference in RestoreFoundrySelection

Agent: csharp

Category: bug

File: src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:612-620

Description: FirstOrDefault can return null but matchingModel is correctly initialized to null and the assignment at line 628 handles it properly. This is actually safe code.

Suggestion: Code is actually safe - matchingModel can be null and FoundryLocalPicker.SelectedModel can accept null. Consider keeping pattern for clarity.

Confidence: 60%

Rule: bug_null_pointer_csharp


🟡 MEDIUM - Implicit default value in setter

Agent: csharp

Category: quality

File: src/settings-ui/Settings.UI.Library/PasteAIProviderDefinition.cs:53

Description: ServiceType property setter silently changes null/whitespace to 'OpenAI' without documentation. This behavior should be documented.

Suggestion: Add XML documentation to explain the default 'OpenAI' behavior, or consider making the default behavior more explicit.

Confidence: 60%

Rule: qual_misleading_names_csharp


🟡 MEDIUM - FirstOrDefault used on query results with proper null check

Agent: csharp

Category: bug

File: src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:895-898

Description: FirstOrDefault() is called on providers collection. The code at line 896 properly checks for null before further operations, and line 901 uses null-conditional access.

Suggestion: Code is actually safe due to null check at line 896 and null-coalescing at line 901.

Confidence: 65%

Rule: cs_check_query_results_before_accessing_ind


🟡 MEDIUM - FirstOrDefault() used where SingleOrDefault() might be appropriate

Agent: csharp

Category: quality

File: src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:169

Description: FirstOrDefault() is used to find a custom action by ID, which should be unique. SingleOrDefault() would document and enforce the uniqueness assumption.

Suggestion: Consider using SingleOrDefault() instead of FirstOrDefault() if the ID is expected to be unique.

Confidence: 60%

Rule: cs_use_first_single_instead_of_firstordefau


🟡 MEDIUM - Catching generic Exception

Agent: csharp

Category: quality

Why this matters: Catching generic exceptions can mask bugs and make debugging difficult.

File: src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/SemanticKernelPasteProvider.cs:143

Description: Catching broad Exception type when retrieving chat service by model ID. This masks specific failures that might need different handling.

Suggestion: Catch specific exception types (InvalidOperationException, KeyNotFoundException) instead of base Exception, or add logging to understand what exceptions occur.

Confidence: 65%

Rule: csharp_catch_exception_base


🟡 MEDIUM - Magic number 1024 for image dimensions (6 occurrences)

Agent: csharp

Category: quality

Why this matters: Magic numbers obscure intent and make changes error-prone when the same value is used in multiple places.

📍 View all locations
File Description Suggestion Confidence
src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/SemanticKernelPasteProvider.cs:103-104 Magic numbers 1024 used for default image width and height. Should be extracted to named constants f... Extract to constants: private const int DefaultImageWidth = 1024; private const int DefaultImageHeig... 75%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:277 Magic number 1 used for clipboard timer interval in seconds. Extract to constant: private const int ClipboardTimerIntervalSeconds = 1; 60%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:751 Magic number 1.5 used for AI action minimum task time. Note: Located at line 751, not 635. Extract to constant: private const double AIActionMinTaskTimeSeconds = 1.5; 65%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:680 Magic number 30 used for temporary file cleanup delay timeout. Extract to constant: private const int CleanupDelaySeconds = 30; 70%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:275-278 Magic number 260 used for MAX_PATH buffer in file dialog. Comment exists but no constant. Extract to constant: private const int MaxPath = 260; // Windows MAX_PATH 65%
src/settings-ui/Settings.UI.Library/PasteAIProviderDefinition.cs:31-32 Hard-coded 1024 values for default image width and height. Should be extracted to constants. Extract to constants for reusability and maintainability 70%

Rule: qual_magic_numbers_csharp


🔵 LOW - Magic string comparisons for service types (4 occurrences)

Agent: csharp

Category: quality

Why this matters: Using enums improves type safety and enables compile-time checking.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:313-316 String literals 'OpenAI' and 'AzureOpenAI' used in string comparisons instead of using the AIService... Use AIServiceType enum comparison via ServiceTypeKind property or convert using ToAIServiceType() me... 65%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:668 String literal 'FoundryLocal' used in equality check when AIServiceType enum already exists and is u... Use AIServiceType enum for type-safe comparison, similar to the pattern on line 677: IsServiceTypeAl... 75%
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:852 String literal 'OpenAI' used as default. Should use enum constant. Use enum: AIServiceType.OpenAI.ToConfigurationString() 68%
src/settings-ui/Settings.UI.Library/PasteAIProviderDefinition.cs:19 String literal 'OpenAI' used as default service type in field initialization. This duplicates the de... Extract to a constant or use AIServiceType enum for consistency 62%

Rule: cs_use_enums_instead_of_magic_strings


ℹ️ 18 issue(s) outside PR diff (click to expand)

These issues were found in lines not modified in this PR.

🟡 MEDIUM - Unsafe registry value casting without type validation (2 occurrences)

Agent: security

Category: security

Why this matters: Avoids exceptions as control flow and handles invalid input gracefully.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:346-347 Registry.GetValue returns object type and is directly cast to int without validation. Default value ... Use safe casting pattern: `var value = Registry.GetValue(registryKey, "EnableClipboardHistory", 0); ... 85%
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:359-364 Registry.GetValue result is directly cast to int without type checking. Null check exists but no val... Use safe casting: `if (allowClipboardHistory is int intValue) { return intValue == 0; } return false... 78%

Rule: cs_use_tryparse_for_string_conversions


🟡 MEDIUM - Magic strings duplicated across multiple methods (2 occurrences)

Agent: quality

Category: quality

Why this matters: Makes code harder to change safely.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:582-722 Status text strings appear in multiple locations without being extracted to constants. Same status m... Extract to private const fields: private const string StatusTextNoModelsDetected = "No local models ... 80%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:548-642 Multiple methods repeat identical pattern: null-check FoundryLocalPicker, then update multiple prope... Extract a helper method: `private void UpdateFoundryPickerState(bool isLoading, bool isAvailable, st... 75%

Rule: quality_inconsistent_naming


🟡 MEDIUM - Event handler subscription without unsubscription

Agent: csharp

Category: quality

Why this matters: This pattern commonly causes memory leaks in UI components.

File: src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml.cs:63-70

Description: ViewModel.PropertyChanged and ViewModel.PreviewRequested events are subscribed in constructor but may not be unsubscribed if the UserControl is reused or disposed.

Suggestion: Implement IDisposable or override OnUnloaded to unsubscribe from ViewModel.PropertyChanged and ViewModel.PreviewRequested events.

Confidence: 65%

Rule: csharp_dispose_not_called


🟡 MEDIUM - Empty catch block in Dispose method (4 occurrences)

Agent: csharp

Category: quality

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:1048-1051 Catch block in the Dispose method swallows exceptions from cancellation without any handling or logg... Add a debug-level log: catch (Exception ex) { Logger.LogDebug("Cancellation failed during disposal",... 65%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:299-301 Catch block catches exceptions without logging or handling them for file deletion operation. Comment... Add a comment explaining why exceptions are being ignored, or consider logging at debug level with c... 62%
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:814 Multiple catch blocks throughout the file swallow exceptions without logging (lines 308, 321, 349, 3... Add meaningful error logging or at minimum a code comment explaining the intent: catch (Exception ex... 70%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:144-147 Code has a comment explaining intent but no logging mechanism for debugging purposes. Add meaningful logging or explanation: catch (Exception ex) { Logger.LogDebug("Failed to refresh bin... 60%

Rule: cs_avoid_empty_catch_blocks


🟡 MEDIUM - Potential null reference in RestoreFoundrySelection

Agent: csharp

Category: bug

File: src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:612-620

Description: FirstOrDefault can return null but matchingModel is correctly initialized to null and the assignment at line 628 handles it properly. This is actually safe code.

Suggestion: Code is actually safe - matchingModel can be null and FoundryLocalPicker.SelectedModel can accept null. Consider keeping pattern for clarity.

Confidence: 60%

Rule: bug_null_pointer_csharp


🟡 MEDIUM - FirstOrDefault used on query results with proper null check

Agent: csharp

Category: bug

File: src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:895-898

Description: FirstOrDefault() is called on providers collection. The code at line 896 properly checks for null before further operations, and line 901 uses null-conditional access.

Suggestion: Code is actually safe due to null check at line 896 and null-coalescing at line 901.

Confidence: 65%

Rule: cs_check_query_results_before_accessing_ind


🟡 MEDIUM - FirstOrDefault() used where SingleOrDefault() might be appropriate

Agent: csharp

Category: quality

File: src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:169

Description: FirstOrDefault() is used to find a custom action by ID, which should be unique. SingleOrDefault() would document and enforce the uniqueness assumption.

Suggestion: Consider using SingleOrDefault() instead of FirstOrDefault() if the ID is expected to be unique.

Confidence: 60%

Rule: cs_use_first_single_instead_of_firstordefau


🟡 MEDIUM - Missing XML documentation for public class and constructor

Agent: csharp

Category: docs

Why this matters: Enables IntelliSense and consistent API usage across assemblies.

File: src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs:23-41

Description: The public sealed class CustomActionTransformService and its public constructor lack XML documentation.

Suggestion: Add

tag to the class and and tags to the constructor.

Confidence: 65%

Rule: cs_xml_documentation_for_public_apis


🟡 MEDIUM - Magic string 'FoundryLocal' in equality check (2 occurrences)

Agent: csharp

Category: quality

Why this matters: Improves type safety and discoverability.

📍 View all locations
File Description Suggestion Confidence
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:668 String literal 'FoundryLocal' used in equality check when AIServiceType enum already exists and is u... Use AIServiceType enum for type-safe comparison, similar to the pattern on line 677: IsServiceTypeAl... 75%
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs:852 String literal 'OpenAI' used as default. Should use enum constant. Use enum: AIServiceType.OpenAI.ToConfigurationString() 68%

Rule: cs_use_enums_instead_of_magic_strings


🟡 MEDIUM - Magic number 1 for timer interval (3 occurrences)

Agent: csharp

Category: quality

Why this matters: Magic numbers obscure intent and make changes error-prone.

📍 View all locations
File Description Suggestion Confidence
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:277 Magic number 1 used for clipboard timer interval in seconds. Extract to constant: private const int ClipboardTimerIntervalSeconds = 1; 60%
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs:751 Magic number 1.5 used for AI action minimum task time. Note: Located at line 751, not 635. Extract to constant: private const double AIActionMinTaskTimeSeconds = 1.5; 65%
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs:275-278 Magic number 260 used for MAX_PATH buffer in file dialog. Comment exists but no constant. Extract to constant: private const int MaxPath = 260; // Windows MAX_PATH 65%

Rule: qual_magic_numbers_csharp



Review ID: 0e2fcd21-993a-49b1-b1ab-9480f10905b9
Rate it 👍 or 👎 to improve future reviews | Powered by diffray

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds text-to-image generation support to Advanced Paste's AI capabilities, enabling users to generate images from text descriptions using OpenAI and Azure OpenAI providers.

Key Changes:

  • Introduces PasteAIUsage enum to distinguish between chat completion and text-to-image operations
  • Adds configurable image dimensions (width/height) with default 1024x1024 resolution
  • Implements image display and pasting functionality for generated images returned as data URIs

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/settings-ui/Settings.UI.Library/PasteAIUsage.cs Defines new enum for AI usage types
src/settings-ui/Settings.UI.Library/PasteAIUsageExtensions.cs Provides conversion methods between enum and config strings
src/settings-ui/Settings.UI.Library/PasteAIProviderDefinition.cs Adds Usage, ImageWidth, and ImageHeight properties to provider configuration
src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs Updates provider synchronization to include new properties
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml Adds UI controls for usage selection and image resolution configuration
src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPastePage.xaml.cs Implements visibility logic and event handlers for text-to-image settings
src/settings-ui/Settings.UI/Converters/PasteAIUsageToStringConverter.cs Converts usage enum to localized display strings in Settings UI
src/settings-ui/Settings.UI/Strings/en-us/Resources.resw Adds localized strings for usage types and image dimensions
src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs Implements image display and pasting logic for data URI images
src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/SemanticKernelPasteProvider.cs Implements text-to-image generation using Semantic Kernel
src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/PasteAIConfig.cs Extends config with Usage, ImageWidth, and ImageHeight properties
src/modules/AdvancedPaste/AdvancedPaste/Services/CustomActions/CustomActionTransformService.cs Populates new config properties when building provider config
src/modules/AdvancedPaste/AdvancedPaste/Helpers/DataPackageHelpers.cs Adds helper method to create DataPackage from image stream
src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Converters/PasteAIUsageToStringConverter.cs Converts usage enum to localized display strings in AdvancedPaste UI
src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml Updates UI to display images and show usage badges
src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml.cs Simplifies provider selection logic
src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw Adds localized strings for usage types

Comment on lines +81 to +83
case PasteAIUsage.TextToImage:
var imageDescription = string.IsNullOrWhiteSpace(prompt) ? inputText : $"{inputText}. {prompt}";
return await ProcessTextToImageAsync(kernel, imageDescription, cancellationToken);
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a provider is configured with TextToImage usage for non-OpenAI/AzureOpenAI service types (e.g., through manual config file editing), this will attempt to retrieve ITextToImageService from the kernel but no such service will be registered (see lines 227-242 in CreateKernel), causing a runtime exception. Consider adding validation to ensure TextToImage usage is only allowed for OpenAI and AzureOpenAI providers, or add error handling to provide a clear error message.

Copilot uses AI. Check for mistakes.
{
try
{
var base64Data = CustomFormatResult.Split(',')[1];
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data URI parsing assumes the format 'data:type;base64,data' and directly accesses index [1] after splitting on comma. This will throw an IndexOutOfRangeException if the data URI doesn't contain a comma. Add validation to ensure the split result has at least 2 elements before accessing index [1].

Suggested change
var base64Data = CustomFormatResult.Split(',')[1];
var parts = CustomFormatResult.Split(',');
if (parts.Length < 2)
{
Logger.LogWarning("CustomFormatResult does not contain expected data URI payload segment.");
return null;
}
var base64Data = parts[1];

Copilot uses AI. Check for mistakes.
Comment on lines +663 to +664
var base64Data = text.Split(',')[1];
var bytes = Convert.FromBase64String(base64Data);
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The data URI parsing assumes the format 'data:type;base64,data' and directly accesses index [1] after splitting on comma. This will throw an IndexOutOfRangeException if the data URI doesn't contain a comma. Add validation to ensure the split result has at least 2 elements before accessing index [1].

Copilot uses AI. Check for mistakes.
{
var base64Data = CustomFormatResult.Split(',')[1];
var bytes = Convert.FromBase64String(base64Data);
var stream = new System.IO.MemoryStream(bytes);
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MemoryStream created here is not being disposed. This can lead to resource leaks. Consider using a 'using' statement or calling Dispose() to ensure proper cleanup of the stream.

Suggested change
var stream = new System.IO.MemoryStream(bytes);
using var stream = new System.IO.MemoryStream(bytes);

Copilot uses AI. Check for mistakes.
}
else if (imageContent.Uri != null)
{
using var client = new HttpClient();
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating HttpClient with 'using' is an anti-pattern that can lead to socket exhaustion. HttpClient is designed to be reused and should be instantiated once and reused throughout the life of an application. Consider using a static HttpClient instance or IHttpClientFactory.

Suggested change
using var client = new HttpClient();
var client = new HttpClient();

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +108
var width = _config.ImageWidth > 0 ? _config.ImageWidth : 1024;
var height = _config.ImageHeight > 0 ? _config.ImageHeight : 1024;
var settings = new OpenAITextToImageExecutionSettings
{
Size = (width, height),
};
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback values for image dimensions only check if they're greater than 0, but don't validate if they're within reasonable bounds. OpenAI's image generation APIs have specific size constraints. Consider adding validation to ensure width and height are within acceptable ranges for the specific model being used, and providing clear error messages if invalid dimensions are provided.

Copilot uses AI. Check for mistakes.
Comment on lines +665 to +667
var stream = new System.IO.MemoryStream(bytes);
var dataPackage = DataPackageHelpers.CreateFromImage(Windows.Storage.Streams.RandomAccessStreamReference.CreateFromStream(stream.AsRandomAccessStream()));
await CopyPasteAndHideAsync(dataPackage);
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MemoryStream created here is not being disposed. This can lead to resource leaks. Consider using a 'using' statement or calling Dispose() to ensure proper cleanup of the stream.

Copilot uses AI. Check for mistakes.
public string Usage
{
get => _usage;
set => SetProperty(ref _usage, string.IsNullOrWhiteSpace(value) ? "ChatCompletion" : value); // TODO: Localization support
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO comment is misleading. The Usage property stores the configuration string value ("ChatCompletion" or "TextToImage"), which should not be localized. Localization is already properly handled in the UI converters (PasteAIUsageToStringConverter) that display these values to users. Consider removing this comment.

Suggested change
set => SetProperty(ref _usage, string.IsNullOrWhiteSpace(value) ? "ChatCompletion" : value); // TODO: Localization support
set => SetProperty(ref _usage, string.IsNullOrWhiteSpace(value) ? "ChatCompletion" : value);

Copilot uses AI. Check for mistakes.
Comment on lines +527 to +540
<TextBox
x:Name="PasteAIImageWidthTextBox"
x:Uid="AdvancedPaste_ImgOutputWidth"
MinWidth="96"
Text="{x:Bind ViewModel.PasteAIProviderDraft.ImageWidth, Mode=TwoWay}" />
<TextBlock
Margin="0,0,0,8"
VerticalAlignment="Bottom"
Text="x" />
<TextBox
x:Name="PasteAIImageHeightTextBox"
x:Uid="AdvancedPaste_ImgOutputHeight"
MinWidth="96"
Text="{x:Bind ViewModel.PasteAIProviderDraft.ImageHeight, Mode=TwoWay}" />
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TextBox controls for ImageWidth and ImageHeight lack input validation. Users can enter non-numeric values, negative numbers, zero, or extremely large values, which could cause binding errors or runtime issues. Consider adding input validation to ensure only positive integers within reasonable bounds (e.g., 64-4096) are accepted.

Copilot uses AI. Check for mistakes.
Comment on lines +333 to +342
var comboBox = (ComboBox)sender;
if (comboBox.SelectedValue is string usage && usage == "TextToImage")
{
ViewModel.PasteAIProviderDraft.EnableAdvancedAI = false;
PasteAIEnableAdvancedAICheckBox.IsEnabled = false;
}
else
{
PasteAIEnableAdvancedAICheckBox.IsEnabled = true;
}
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is duplicated logic between this event handler and the UpdatePasteAIUIVisibility method (lines 389-397). Both are setting EnableAdvancedAI to false and disabling the checkbox when usage is TextToImage. Consider consolidating this logic to avoid maintenance issues and potential inconsistencies.

Suggested change
var comboBox = (ComboBox)sender;
if (comboBox.SelectedValue is string usage && usage == "TextToImage")
{
ViewModel.PasteAIProviderDraft.EnableAdvancedAI = false;
PasteAIEnableAdvancedAICheckBox.IsEnabled = false;
}
else
{
PasteAIEnableAdvancedAICheckBox.IsEnabled = true;
}
UpdatePasteAIUIVisibility();

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants