Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Live preview while editing AXAML files #150

Open
wants to merge 55 commits into
base: main
Choose a base branch
from
Open

Live preview while editing AXAML files #150

wants to merge 55 commits into from

Conversation

tomlm
Copy link
Collaborator

@tomlm tomlm commented Nov 17, 2024

TLDR: This checkin adds ability to preview consolonia .axaml files while editing them.

Backstory
I ended up implementing multiple layered approaches to try and acheive this goal.

Attempt 1 - try and not have the built-avalonia viewer puke

Basically, the reason the previewer pukes was because of a variety of issues.

  • The previewer looks for static AppBuilder function to instantiate your project, and gallery didn't have one.
  • we were using Console APIs that don't exist in the context of VS and so it caused the previewer host to blow up.
  • our fonts and lines and such are drawn with essentially 1 pixel = 1 char mapping, which doesn't map to an actual pixelated display.
  • We don't set default foreground and background, causing nothing to be displayed.

Changes Made:

  • I added Consolonia.Designer assembly
  • Added .UseConsoloniaDesigner(). This effectively detects when are app is being loaded by the previewer in design mode Designer.IsDesignMode and makes the following tweaks to the environment only when in design mode
  1. it loads normal avalonia desktop environment instead of consolonia
  2. It registers a DummyConsole which doesn't use Console API
  3. It sets styles to set a default Font, backround, foreground
    The effect of this is that Text and colors basically render correctly, but issues are:
  • Scaling is way off
  • Anything having to do with lines or rectangles are way off (listbox, boxlinebrush, etc.)
  • I tried a bunch of things around scaling, but it was super janky
    Screenshot 2024-11-16 191113

So...basically, it's not as shitty as having a broken pane, but not really useful at all.

Attempt 2 - Avalonia PreviewControl displayed via Design.Preview

I had noticed that Avalonia has the following markup:

<Design.PreviewWith>
<...alternate markup
</Design.PreviewWith>

That got me thinking that I could create Avalonia control which talks to a consolonia app, and if you simply dropped that control in there it would give you the preview.

Changes made

  • I added a Consolonia.PreviewHost application which allows you to have a live preview of a xaml file. It notices file changes and ssembly changes and reloads itself automatically
  • Added Argument to that app (--buffer width height) which basically asks it to dump the PixelBuffer as output
  • Added to Consolonia.Designer a custom control ConsolePreview. This guy launches the PreviewHost with --buffer flag, gets the PixelBuffer and renders it as live Avalonia markup as a series of TextBlocks.
  • To test this control I created a project Avalonia.PreviewHost. This is simply an avalonia app which uses the ConsolePreview control and Consolonia.PreviewHost to render in a pane.

The Consolonia.PreviewHost and Avalonia.PreviewHost look and work great.
Here's some clips:

Consolonia.PreviewHost

Consolonia PreviewHost

Avalonia.PreviewHost

Avalonia PreviewHost

So this is looking super promising. There are no real issues yet except to hook it up.

Well.

😠😭💔

The avalonia preview window only honors the Design.WithPreview tag for Style and Resources, not for user controls. It's a couple line change in avalonia to add the support, but it doesn't work out of the box.

ConsolePreview with Design.WithPreview markup

To show what it looks like, here's clip where I used ConsolePreview control as the WithPreview for a Resource Dictionary.

ConsolePreviewControl
This highlights several pretty serious issues:

  1. it's slow as f__k.
  2. As soon as you start typing it says "there's an error"
  3. you have to recompile to get it render again.
  4. There still are artifacts.

This was a pretty despairing point for me, as I was starting to think this was a no-go. I started to think about creating my own VS integration (Which I have done in the past, but it's a painful thing to do.) If only I could just get the console app to work directly in vs.......hey.....wait a minute.....

Attempt 3 - Host the console preview app in a terminal window.

With no more code changes, I tried just running the Consolonia.PreviewHost app in a terminal window. The result is actually pretty magic, and it works in both Visual Studio and Visual Studio Code.

Visual Studio

With single file preview
Studio-Single

With project preview
Studio-Project

Visual Studio Code

With single file preview
Code-SingleFile

With project preview
Code-Project

Pros:

  • It automatically refreshes on build and file change
  • You can interact with it
  • 100% fidelity, all animations etc work.

Cons:

  • ???

Changes made

none! 🤪

The only issues are:

  • it doesn't sync to the current file you are looking at.

At any rate, I think this is pretty respectable solution, just use Consolonia.PreviewHost.exe for live previews, and why not? We should be dog fooded our own tool to make tools, no?

I may at a future point try and create an integration that tightly bounds the active file to the preview window, but for now I'm going to declare victory.

I'm keeping the Avalonia.PreviewHost and PreivewControl because I spent a lot of time to get them working, and they don't hurt anything.

The stuff I did to make it so the built-in previewer doesn't barf I think is useful, but again very easy to not use if you don't want it. It's all isolated in the Consolonia.Designer assembly, and so you can ignore it if you don't like it.

BTW: I made Consolonia.PreviewHost a dotnet tool, so you can install it with

dotnet tool install --global --add-source tools\Consolonia.PreviewHost\Bin\Release  Consolonia.PreviewHost

OBTW: I also added 2 new color themes: Fluent and Material. They look very similar but I think it looks way better.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a DummyConsole class for basic console-like functionality.
    • Added ConsolePreview user control for previewing console output in Visual Studio.
    • Implemented JSON serialization for various classes including DrawingBoxSymbol, Pixel, and PixelForeground.
    • Added support for design-time features in multiple XAML files.
    • New FluentTheme and MaterialTheme classes for enhanced styling options.
    • Added a new FileViewModel class to support file management in the preview host.
    • Introduced a new ResourceTest.axaml for design-time previews of console output.
  • Improvements

    • Enhanced pixel buffer management and rendering logic in the RenderTarget class.
    • Updated theme management in the App class to use MaterialTheme.
    • Streamlined application startup process in Program.cs for better configurability.
  • Bug Fixes

    • Improved null handling in equality checks and serialization tests across multiple classes.
  • Tests

    • Added comprehensive unit tests for DrawingContextImpl, PixelBuffer, and SimpleSymbol classes to ensure functionality and serialization integrity.
    • Expanded test coverage for JSON serialization in PixelBackground and SimpleSymbol classes.

Copy link
Contributor

coderabbitai bot commented Nov 17, 2024

Walkthrough

The changes in this pull request primarily involve modifications to the Consolonia.Core and Consolonia.Designer projects. Key updates include the addition of assembly attributes to enhance internal member visibility, updates to project dependencies, and significant alterations to classes related to drawing and pixel management. New serialization functionalities for JSON handling have been introduced, along with various tests to validate these changes. Additionally, several new files and project configurations have been added to support design-time functionality and improve the overall structure of the solution.

Changes

File Change Summary
src/Consolonia.Core/Assembly.cs Added assembly attribute [assembly: InternalsVisibleTo("Consolonia.Designer")].
src/Consolonia.Core/Consolonia.Core.csproj Removed Avalonia.FreeDesktop, added Avalonia.ReactiveUI, removed SkiaSharp.NativeAssets.Linux.NoDependencies, added Newtonsoft.Json version 13.0.3.
src/Consolonia.Core/Drawing/DrawingContextImpl.cs Updated constructor to remove PixelBuffer parameter; modified pixel blending logic in DrawBitmap, DrawRectangle, and DrawStringInternal methods.
src/Consolonia.Core/Drawing/PixelBufferImplementation/ColorConverter.cs Added ColorConverter class for JSON serialization of Color.
src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs Changed DrawingBoxSymbol from struct to class; updated properties and methods for JSON serialization.
src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs Added [JsonConverter(typeof(SymbolConverter))] attribute to ISymbol interface.
src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs Changed Pixel from struct to class; updated constructors and added new static properties.
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs Changed PixelBackground from struct to class; updated properties to use init accessors.
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs Added new constructor and updated indexers for pixel access; marked some properties with [JsonIgnore].
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs Added PixelBufferConverter class for custom serialization of PixelBuffer.
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs Changed PixelForeground from struct to class; updated properties to use init accessors.
src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs Changed SimpleSymbol from struct to class; added JSON converter attribute.
src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs Added SymbolConverter class for JSON serialization of ISymbol.
src/Consolonia.Core/Drawing/RenderTarget.cs Removed _bufferBuffer; updated methods for pixel buffer management.
src/Consolonia.Core/Dummy/DummyConsole.cs Introduced DummyConsole class implementing IConsole.
src/Consolonia.Core/Helpers/Extensions.cs Updated Print and MeasureText methods to reflect changes in text decoration handling.
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs Changed visibility to public; added PixelBuffer property; refactored Dispose method.
src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs Added OnFrameworkInitializationCompleted method for design-time support.
src/Consolonia.Core/Infrastructure/IConsole.cs Updated Print method signature; added WriteText method.
src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs Updated methods for console operations and added emoji support checks.
src/Consolonia.Core/Properties/launchSettings.json Added configuration for project execution.
src/Consolonia.Core/Text/Esc.cs Refactored ConsoleUtils class to Esc and added new cursor manipulation methods.
src/Consolonia.Designer/Assembly.cs Added [assembly: CLSCompliant(false)] attribute.
src/Consolonia.Designer/ConsolePreview.cs Introduced ConsolePreview class for rendering console output in design mode.
src/Consolonia.Designer/Consolonia.Designer.csproj Created new project file with package references for Avalonia and Newtonsoft.Json.
src/Consolonia.Designer/Extensions.cs Added UseConsoloniaDesigner method for application builder configuration.
src/Consolonia.Gallery/App.cs Updated theme management from TurboVisionTheme to MaterialTheme.
src/Consolonia.Gallery/Consolonia.Gallery.csproj Modified resource inclusion pattern and added project references.
src/Consolonia.Gallery/Gallery/GalleryViews/*.axaml Added XML namespaces for design-time support in multiple XAML files.
src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml.cs Minor modification: added newline at the end of the file.
src/Tests/Consolonia.Core.Tests/*.cs Added various tests for JSON serialization and updated existing tests to handle new property names.
src/Tools/Avalonia.PreviewHost/*.cs Introduced files for the Avalonia preview host application, including app structure and view models.
src/Consolonia.Themes.TurboVision/Themes/*.axaml Added new theme files and resources for Fluent and Material themes.

Possibly related PRs

  • Fix strikethrough implementation #134: This PR addresses the implementation of strikethrough and underline text effects, which is directly related to the changes in the main PR that enhance visibility for the Consolonia.Designer assembly. The modifications in DrawingContextImpl for handling text decorations are relevant to the internal visibility improvements made in the main PR.
  • Add support for Surrogate Unicode sequences  #139: This PR introduces support for surrogate Unicode sequences, which involves changes to the DrawingContextImpl class that relate to how text is processed and rendered. This is relevant to the main PR's focus on enhancing internal visibility for the Consolonia.Designer assembly, as it may involve similar rendering and text handling improvements.

Suggested reviewers

  • goe5mu

🐰 In the land of code where bunnies play,
Changes abound in a bright new way.
With pixels and colors, we hop and we cheer,
For Consolonia's magic is finally here!
From tests to themes, our joy will not cease,
Let's celebrate changes, may they bring us peace! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

tomlm and others added 3 commits November 18, 2024 22:52
Co-authored-by:  <+@users.noreply.github.com>
Co-authored-by:  <+@users.noreply.github.com>
@tomlm tomlm marked this pull request as ready for review November 19, 2024 07:03
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 40

🧹 Outside diff range and nitpick comments (71)
src/Consolonia.Core/Assembly.cs (1)

7-7: Consider addressing the CLS compliance TODO comment.

The existing TODO comment about CLS compliance should be addressed. CLS compliance ensures better interoperability with other .NET languages.

Would you like me to help analyze the codebase for CLS compliance issues and create a GitHub issue to track this work?

src/Tools/Avalonia.PreviewHost/ViewModels/FileViewModel.cs (1)

7-10: Consider adding XML documentation.

While the property names are descriptive, XML documentation would help clarify:

  • The relationship between _fullName and _name
  • Expected format of the paths
  • Null handling behavior

Example improvement:

+    /// <summary>
+    /// Represents a file in the preview system.
+    /// </summary>
     public partial class FileViewModel : ObservableObject
     {
+        /// <summary>
+        /// Gets or sets the full path of the file.
+        /// </summary>
         [ObservableProperty] private string? _fullName;

+        /// <summary>
+        /// Gets or sets the file name without the path.
+        /// </summary>
         [ObservableProperty] private string? _name;
     }
src/Consolonia.Themes.TurboVision/Themes/Material/Material.axaml (1)

3-5: Add preview content to enable design-time visualization.

The empty Design.PreviewWith section should include sample controls to help visualize the Material theme during design time.

 <Design.PreviewWith>
+    <StackPanel Spacing="20">
+        <Button Content="Material Button" />
+        <TextBox Text="Material TextBox" />
+    </StackPanel>
 </Design.PreviewWith>
src/Consolonia.Themes.TurboVision/Themes/Material/MaterialTheme.cs (1)

10-16: Consider if theme-specific initialization is needed.

The constructors properly delegate to the base class, but consider if any Material theme-specific initialization is required, such as setting default colors or styles.

 public MaterialTheme(IServiceProvider serviceProvider) : base(serviceProvider)
 {
+    // Consider initializing Material-specific resources or defaults here
 }
src/Consolonia.Core/Drawing/PixelBufferImplementation/ColorConverter.cs (1)

17-20: Remove unnecessary null-forgiving operator in WriteJson

The Color type is a value type, so the null-forgiving operator is unnecessary.

Apply this change:

 public override void WriteJson(JsonWriter writer, Color value, JsonSerializer serializer)
 {
-    writer.WriteValue(value!.ToString());
+    writer.WriteValue(value.ToString());
 }
src/Consolonia.Gallery/Program.cs (1)

20-28: LGTM: Well-structured configuration with proper design/runtime separation

The BuildAvaloniaApp method changes demonstrate good architectural decisions:

  1. Public visibility enables design-time tooling integration
  2. Conditional compilation properly separates design-time and runtime behaviors
  3. Maintains existing logging and console detection functionality

Consider documenting the different initialization paths in XML comments to help future maintainers understand the design-time vs runtime behavior.

src/Tools/Avalonia.PreviewHost/Program.cs (1)

5-5: Consider making the Program class static

Since this class only contains static members and serves as an application entry point, it should be marked as static to prevent instantiation and better express its intent.

-    internal class Program
+    internal static class Program
🧰 Tools
🪛 GitHub Check: build

[notice] 5-5:
"[ClassNeverInstantiated.Global] Class 'Program' is never instantiated" on /home/runner/work/Consolonia/Consolonia/src/Tools/Avalonia.PreviewHost/Program.cs(5,20)

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryGradientBrush.axaml (1)

21-22: Remove or document the empty StackPanel

The empty StackPanel in Grid.Row="1" appears to serve no purpose. Consider either:

  1. Removing it if not needed
  2. Adding a comment explaining its intended future use
  3. Implementing its intended functionality now
-        
-        <StackPanel Grid.Row="1"/>
src/Consolonia.Designer/Extensions.cs (3)

9-20: Complete the XML documentation

The documentation is incomplete and missing important details:

  • The <param name="builder"> tag lacks description
  • The <returns> tag lacks description
  • There's an incomplete note about package references

Apply this diff to improve the documentation:

 /// <summary>
-    This method is used to initialize consolonia
+    Initializes Consolonia with design-time support
 /// </summary>
-/// <param name="builder"></param>
+/// <param name="builder">The AppBuilder instance to configure</param>
-/// <returns></returns>
+/// <returns>The configured AppBuilder instance</returns>
 /// <remarks>
     In Design mode it will load up avalonia instead of consolonia so that avalonia
     previewer will attempt to render it. This mostly only works for text layout.
     In release mode it will load consolonia, removing the dependency on the desktop avalonia subsystem.
-    NOTE: The package references for
+    NOTE: Ensure appropriate package references for both design-time and runtime scenarios are included.
 /// </remarks>

23-23: Clean up or document the commented-out condition

There's a commented-out condition next to Design.IsDesignMode. If this is important for future reference, consider documenting why it was replaced. Otherwise, remove it for cleaner code.

-            if (Design.IsDesignMode) //AppDomain.CurrentDomain.FriendlyName == "Avalonia.Designer.HostApp")
+            if (Design.IsDesignMode)

24-28: Consider adding error handling and logging

The design-time configuration chain might benefit from error handling and logging to help diagnose preview issues mentioned in the PR objectives.

Consider wrapping the configuration chain in a try-catch block:

             if (Design.IsDesignMode)
+            {
+                try
+                {
                     return builder
                         .UsePlatformDetect()
                         .WithInterFont()
                         .LogToTrace();
+                }
+                catch (Exception ex)
+                {
+                    System.Diagnostics.Trace.WriteLine($"Design-time initialization failed: {ex}");
+                    throw;
+                }
+            }
src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs (1)

8-11: Consider backwards compatibility for IsAssignableTo.

The IsAssignableTo method was introduced in .NET 5. For better compatibility, consider using IsAssignableFrom instead:

-            return objectType.IsAssignableTo(typeof(ISymbol));
+            return typeof(ISymbol).IsAssignableFrom(objectType);
src/Consolonia.Core/Infrastructure/IConsole.cs (1)

32-32: Document the purpose and usage of WriteText method.

The new WriteText method's purpose and relationship to the existing Print method isn't immediately clear. Consider adding XML documentation to explain:

  • Its intended use case
  • How it differs from Print
  • Whether it respects current cursor position
  • Its role in the preview functionality

Add documentation like this:

+        /// <summary>
+        /// Writes text directly to the console without styling or positioning.
+        /// </summary>
+        /// <param name="str">The text to write</param>
         void WriteText(string str);
src/Consolonia.Gallery/App.cs (1)

Line range hint 13-35: Consider separating design-time configuration

Given that this PR introduces live preview functionality, consider extracting design-time specific configurations into a separate method or class. This would make it easier to maintain different behaviors between runtime and design-time environments.

Example approach:

  1. Create a DesignTimeConfiguration class
  2. Move theme initialization logic there for design-time scenarios
  3. Use the detection method mentioned in PR objectives to conditionally apply configurations

This separation would make it easier to:

  • Maintain different theme configurations for runtime vs. design-time
  • Debug preview-related issues
  • Test both modes independently
src/Tools/Avalonia.PreviewHost/App.axaml.cs (2)

1-12: Enable nullable reference type annotations.

Given the usage of nullable reference types in the code (e.g., object? in Desktop_Exit), consider enabling nullable reference type annotations by adding <Nullable>enable</Nullable> to the project file or #nullable enable at the file level for consistent null-safety across the codebase.


1-43: Consider improving preview lifecycle management.

The current implementation mixes UI initialization with preview management. Consider:

  1. Extract preview management into a dedicated service
  2. Implement IDisposable pattern for proper resource cleanup
  3. Add lifecycle events for preview state changes

This would improve maintainability and make the preview functionality more reusable.

src/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml (1)

4-41: Consider adding usage guidelines in comments.

The Material Design color scheme is well-implemented, but consider adding comments that describe:

  • The intended usage context for each brush (e.g., when to use ThemeAlternativeBackgroundBrush)
  • Any specific accessibility considerations
  • Color variation guidelines for different states (hover, pressed, etc.)

Example addition:

 <!-- Material Design Color Scheme -->
+<!-- Usage Guidelines:
+     - ThemeBackgroundBrush: Primary background for all standard surfaces
+     - ThemeAlternativeBackgroundBrush: Secondary background for nested or alternate content
+     - ThemeActionBackgroundBrush: Use for primary actions and interactive elements
+     For hover states, consider using opacity variants of these colors
+-->
src/Consolonia.Themes.TurboVision/Themes/Fluent/FluentColors.axaml (1)

12-19: Fix inconsistent comments for identical colors.

The ThemeChooserBackgroundBrush and ThemeButtonBackgroundBrush use the same color (#0078D4) but have different comments:

  • "Fluent System Blue" for ThemeChooserBackgroundBrush
  • "Fluent System Light Gray" for ThemeButtonBackgroundBrush

Please update the comment for ThemeButtonBackgroundBrush to "Fluent System Blue" for consistency.

src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (1)

49-49: Consider making Width a readonly property

Since Width is calculated from the readonly Text property and never changes after initialization, consider making it a readonly property:

-[JsonIgnore] public ushort Width { get; init; }
+[JsonIgnore] public ushort Width { get; }
🧰 Tools
🪛 GitHub Check: build

[notice] 49-49:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs(49,49)

src/Consolonia.Core/Text/Esc.cs (1)

8-12: Consider a more descriptive class name and improve documentation.

While Esc is concise, a name like AnsiEscapeSequences would be more self-documenting. Also, consider adding XML documentation for the new control character constants explaining their purpose and usage.

-    internal static class Esc
+    /// <summary>
+    /// Provides ANSI escape sequences and control characters for terminal manipulation.
+    /// </summary>
+    internal static class AnsiEscapeSequences
     {
         // Control
+        /// <summary>
+        /// End of Text (ETX) control character. Used to signal the end of a text transmission.
+        /// </summary>
         public const string EndOfText = "\u0003";
+        /// <summary>
+        /// End of Transmission (EOT) control character. Used to indicate the end of a transmission session.
+        /// </summary>
         public const string EndOfTransmission = "\u0004";
src/Consolonia.Gallery/View/ControlsListView.axaml.cs (1)

28-32: Improve null handling in command line argument initialization.

While the change improves robustness by safely handling the application lifetime type, the null handling can be more elegant.

Consider this more concise approach using null coalescing:

-            var lifetime = Application.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
-            if (lifetime != null)
-                _commandLineArgs = lifetime!.Args!;
-            else
-                _commandLineArgs = Array.Empty<string>();
+            var lifetime = Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
+            _commandLineArgs = lifetime?.Args ?? Array.Empty<string>();

This change:

  1. Adds null check for Application.Current
  2. Removes redundant null assertion operators
  3. Uses null coalescing for cleaner fallback
src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCalendarPicker.axaml (1)

5-7: LGTM! Design-time support properly configured.

The addition of design-time namespaces and markup compatibility is correct and aligns with the PR's objective of enabling live preview functionality.

Consider enhancing the design-time experience by adding design-time properties:

 <UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:galleryViews="clr-namespace:Consolonia.Gallery.Gallery.GalleryViews"
             x:DataType="galleryViews:GalleryCalendarPickerDataContext"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-            mc:Ignorable="d" 
+            mc:Ignorable="d"
+            d:DesignWidth="450"
+            d:DesignHeight="800"
+            d:DataContext="{d:DesignInstance Type=galleryViews:GalleryCalendarPickerDataContext, IsDesignTimeCreatable=True}"
             x:Class="Consolonia.Gallery.Gallery.GalleryViews.GalleryCalendarPicker">

This will:

  • Set explicit dimensions for the design surface
  • Provide design-time data context for better preview experience
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (1)

36-40: Consider modern C# patterns for null checks.

The equality implementation is correct but could be more idiomatic using modern C# patterns.

-public bool Equals(PixelBackground other)
-{
-    if ((object)other is null) return false;
-    return Color.Equals(other.Color) && Mode == other.Mode;
-}
+public bool Equals(PixelBackground? other)
+{
+    return other is not null && Color.Equals(other.Color) && Mode == other.Mode;
+}
src/Consolonia.Core/Dummy/DummyConsole.cs (3)

12-16: Consider sealing the class or documenting inheritance intent.

The class has virtual members (Dispose) but no inheritors. If inheritance is not intended, consider sealing the class. Otherwise, document the extensibility points for future inheritors.

-    public class DummyConsole : IConsole
+    // If no inheritance is needed:
+    public sealed class DummyConsole : IConsole
+    // OR if inheritance is intended:
+    /// <summary>
+    /// Base implementation of IConsole for design-time preview.
+    /// Inherit from this class to customize preview behavior.
+    /// </summary>
+    public class DummyConsole : IConsole
🧰 Tools
🪛 GitHub Check: build

[notice] 12-12:
"[ClassWithVirtualMembersNeverInherited.Global] Class 'DummyConsole' has some virtual members but no inheritors" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Dummy/DummyConsole.cs(12,18)


29-33: Consider removing the CaretVisible setter.

The setter for CaretVisible is a no-op and always returns false. Consider making it a read-only property.

-    public bool CaretVisible
-    {
-        get => false;
-        set { }
-    }
+    public bool CaretVisible => false;

75-88: Document or implement resource cleanup.

The Dispose method contains placeholder comments for resource cleanup. Please either:

  1. Document that no cleanup is needed, or
  2. Implement the necessary resource cleanup

Would you like me to help implement proper resource cleanup or documentation?

src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs (3)

36-45: Consider making font settings configurable

The style setup looks good, but consider making the font family and size configurable through application settings or theme resources for better flexibility.

Example approach:

-                        new Setter(TemplatedControl.FontSizeProperty, 16.0),
-                        new Setter(TemplatedControl.FontFamilyProperty, new FontFamily("Cascadia Mono")),
+                        new Setter(TemplatedControl.FontSizeProperty, 
+                            Styles.TryGetResource("DefaultFontSize", null, out object fontSize) 
+                                ? fontSize : 16.0),
+                        new Setter(TemplatedControl.FontFamilyProperty, 
+                            Styles.TryGetResource("DefaultFontFamily", null, out object fontFamily)
+                                ? fontFamily : new FontFamily("Cascadia Mono")),

47-59: Document or remove experimental code

The commented experimental code contains valuable insights about scaling attempts. Consider either:

  1. Moving it to proper documentation
  2. Creating a GitHub issue to track this enhancement
  3. Removing it if it's no longer relevant

Would you like me to help create a GitHub issue to track this scaling enhancement?


24-25: Consider caching design mode state

The Design.IsDesignMode check could be cached at application startup to avoid repeated lookups, especially if it's used in other parts of the application.

Example approach:

+        private static readonly bool IsInDesignMode = Design.IsDesignMode;
+
         public override void OnFrameworkInitializationCompleted()
         {
             base.OnFrameworkInitializationCompleted();
 
-            if (Design.IsDesignMode)
+            if (IsInDesignMode)
src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTabControl.axaml (1)

3-6: LGTM! The namespace changes enhance design-time support.

The addition of design-time namespaces and explicit assembly specification aligns well with the PR's objective of enabling live preview functionality. These changes follow standard XAML practices for supporting design-time features.

Consider applying similar namespace updates to other AXAML files in the gallery to maintain consistency in design-time support across the application.

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs (2)

24-29: Consider performance optimization for large buffers

The initialization logic is correct and well-documented. For large buffers, you might consider using Array.Fill or similar bulk operations, but for typical console dimensions, the current implementation is fine.

-            for (ushort y = 0; y < height; y++)
-            for (ushort x = 0; x < width; x++)
-                _buffer[x, y] = Pixel.Space;
+            Array.Fill(_buffer.Cast<Pixel>().ToArray(), Pixel.Space);

69-69: Consider caching the Size property

The Size property creates a new Rect instance on every access. Consider caching this value since the dimensions are immutable.

-        [JsonIgnore] public Rect Size => new(0, 0, Width, Height);
+        private readonly Lazy<Rect> _size;
+        [JsonIgnore] public Rect Size => _size.Value;

Then initialize in constructor:

         public PixelBuffer(ushort width, ushort height)
         {
             Width = width;
             Height = height;
             _buffer = new Pixel[width, height];
+            _size = new Lazy<Rect>(() => new Rect(0, 0, Width, Height));
src/Tests/Consolonia.Core.Tests/PixelBufferTests.cs (2)

1-10: Consider extracting test constants.

To improve test maintainability and readability, consider extracting magic numbers (4, 5) and repeated test data into constants at the class level.

 public class PixelBufferTests
 {
+    private const int TEST_BUFFER_WIDTH = 4;
+    private const int TEST_BUFFER_HEIGHT = 5;

14-23: Improve code readability and reduce string duplication.

The nested loops could be more readable with proper indentation, and the coordinate string creation is duplicated.

-            for (ushort y = 0; y < buffer.Height; y++)
-            for (ushort x = 0; x < buffer.Width; x++)
+            for (ushort y = 0; y < buffer.Height; y++)
+            {
+                for (ushort x = 0; x < buffer.Width; x++)
+                {
+                    var coordText = $"{x},{y}";
+                    if (x % 3 == 0)
+                        buffer[x, y] = new Pixel(new SimpleSymbol(coordText), Colors.Blue);
+                    else if (x % 3 == 1)
+                        buffer[x, y] = Pixel.Empty;
+                    else
+                        buffer[x, y] = new Pixel(new SimpleSymbol(coordText), Colors.White, 
+                            FontStyle.Italic, FontWeight.Bold, TextDecorationLocation.Underline);
+                }
+            }
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (2)

34-48: Consider using System.Text.Json for better performance

The code uses Newtonsoft.Json attributes, but System.Text.Json could provide better performance for this immutable class.

-using Newtonsoft.Json;
+using System.Text.Json.Serialization;

-[JsonConverter(typeof(ColorConverter))]
+[JsonConverter(typeof(JsonColorConverter))]

-[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
+[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]

Line range hint 61-76: Address TODO comment regarding default char check

The TODO comment suggests a potential issue with default char handling that needs investigation.

Would you like me to help implement the default char check or create an issue to track this?

Also, consider adding unit tests to verify the behavior with default char values.

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTextBlock.axaml (1)

17-17: Consider removing trailing spaces

The TextBlock contains trailing spaces which are generally not recommended as they can lead to inconsistent rendering and confusion. If you need to demonstrate space handling, consider using a more explicit approach.

-        <TextBlock Text="This is TextBlock  " />
+        <TextBlock Text="This is TextBlock" />
src/Tests/Consolonia.Core.Tests/DrawingBoxSymbolTests.cs (1)

117-124: Enhance test coverage for JSON serialization.

While the basic serialization test is good, consider adding the following test cases for more robust coverage:

  1. Verify the actual JSON structure
  2. Test edge cases (null values, invalid JSON)
  3. Add type handling configuration for interface deserialization

Here's a suggested enhancement:

 [Test]
 public void JsonSerialization()
 {
     var symbol = new DrawingBoxSymbol(0b0000_1111);
     string json = JsonConvert.SerializeObject(symbol);
+    // Verify JSON structure
+    Assert.That(json, Does.Contain("\"Code\":15")); // 0b0000_1111 = 15
+
+    // Test with type handling
+    var settings = new JsonSerializerSettings
+    {
+        TypeNameHandling = TypeNameHandling.Objects
+    };
+    json = JsonConvert.SerializeObject(symbol, settings);
     var deserializedSymbol = JsonConvert.DeserializeObject<ISymbol>(json);
     Assert.That(symbol.Equals(deserializedSymbol));
+
+    // Test invalid JSON
+    Assert.Throws<JsonReaderException>(() => 
+        JsonConvert.DeserializeObject<ISymbol>("{invalid json}"));
 }
src/Consolonia.Core/Helpers/Extensions.cs (1)

97-97: Document the change in default emoji support behavior

The default behavior for supportsComplexEmoji has changed to true when no console is available. While this aligns with the PR's goal of improving preview functionality, it represents a behavioral change that could affect text measurement in design-time scenarios.

Consider:

  1. Adding XML documentation to explain this default behavior
  2. Adding a unit test to verify the behavior when console is null
 public static ushort MeasureText(this string text)
 {
+    // When no console is available (e.g., in design-time), default to supporting complex emoji
+    // to ensure consistent text measurement in preview scenarios
     var console = AvaloniaLocator.Current.GetService<IConsole>();
     bool supportsComplexEmoji = console != null ? console.SupportsComplexEmoji : true;
src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs (1)

99-106: Consider adding more JSON serialization test cases

While the basic serialization test is good, consider adding tests for:

  1. Null values
  2. Different PixelBackgroundMode values
  3. Special colors (e.g., Colors.Transparent)
  4. Verification of the actual JSON structure

Here's a suggested expansion of the test:

 [Test]
 public void JsonSerialization()
 {
     var pixelBackground = new PixelBackground(Colors.Red);
     string json = JsonConvert.SerializeObject(pixelBackground);
     var pixelBackground2 = JsonConvert.DeserializeObject<PixelBackground>(json);
     Assert.That(pixelBackground.Equals(pixelBackground2));
+
+    // Test with transparent color
+    pixelBackground = new PixelBackground(Colors.Transparent);
+    json = JsonConvert.SerializeObject(pixelBackground);
+    pixelBackground2 = JsonConvert.DeserializeObject<PixelBackground>(json);
+    Assert.That(pixelBackground.Equals(pixelBackground2));
+
+    // Test with different modes
+    pixelBackground = new PixelBackground(PixelBackgroundMode.Shaded, Colors.Blue);
+    json = JsonConvert.SerializeObject(pixelBackground);
+    Assert.That(json.Contains("\"Mode\":2")); // Verify JSON structure
+    pixelBackground2 = JsonConvert.DeserializeObject<PixelBackground>(json);
+    Assert.That(pixelBackground.Equals(pixelBackground2));
+
+    // Test null deserialization
+    Assert.That(JsonConvert.DeserializeObject<PixelBackground>(null), Is.Null);
 }
src/Tests/Consolonia.Core.Tests/SimpleSymbolTests.cs (1)

154-184: Consider adding more comprehensive test cases.

While the current tests cover basic serialization scenarios well, consider adding the following test cases to improve robustness:

  1. Negative test cases for malformed JSON
  2. Verification of the actual JSON structure
  3. Explicit type handling for interface deserialization
  4. Null handling tests

Here's an example of additional test cases:

+        [Test]
+        public void JsonSerialization_VerifyStructure()
+        {
+            ISymbol symbol = new SimpleSymbol("a");
+            string json = JsonConvert.SerializeObject(symbol);
+            
+            // Verify JSON structure contains expected properties
+            Assert.That(json, Does.Contain("\"Text\":\"a\""));
+            Assert.That(json, Does.Contain("\"Width\":1"));
+        }
+
+        [Test]
+        public void JsonSerialization_HandlesNull()
+        {
+            string json = JsonConvert.SerializeObject(null);
+            var deserializedSymbol = JsonConvert.DeserializeObject<ISymbol>(json);
+            Assert.That(deserializedSymbol, Is.Null);
+        }
+
+        [Test]
+        public void JsonSerialization_HandlesMalformedJson()
+        {
+            Assert.Throws<JsonReaderException>(() => 
+                JsonConvert.DeserializeObject<ISymbol>("{malformed json}"));
+        }
+
+        [Test]
+        public void JsonSerialization_WithTypeHandling()
+        {
+            ISymbol symbol = new SimpleSymbol("a");
+            var settings = new JsonSerializerSettings 
+            { 
+                TypeNameHandling = TypeNameHandling.All 
+            };
+            string json = JsonConvert.SerializeObject(symbol, settings);
+            var deserializedSymbol = JsonConvert.DeserializeObject<ISymbol>(json, settings);
+            Assert.That(deserializedSymbol.GetType(), Is.EqualTo(typeof(SimpleSymbol)));
+        }
src/Consolonia.NUnit/UnitTestConsole.cs (1)

76-79: Implementation needed for WriteText method

The method currently ignores its input, which seems incomplete. Consider either:

  1. Implementing the required functionality
  2. Adding a TODO comment explaining the rationale
  3. Adding XML documentation to explain why it's intentionally ignored

Would you like help implementing this method or documenting the rationale for ignoring the input?

src/Tests/Consolonia.Core.Tests/PixelTests.cs (1)

156-164: Consider adding more JSON serialization test cases.

While the basic round-trip serialization test is good, consider adding test cases for:

  • Edge cases (e.g., null values, special characters)
  • Different Pixel configurations (e.g., caret pixels, pixels with different background modes)
  • Serialization format validation

Here's a suggested enhancement:

 [Test]
 public void JsonSerialization()
 {
     var pixel = new Pixel(new PixelForeground(new SimpleSymbol('a'), Colors.Red),
         new PixelBackground(Colors.Blue));
     string json = JsonConvert.SerializeObject(pixel);
     var pixel2 = JsonConvert.DeserializeObject<Pixel>(json);
     Assert.That(pixel.Equals(pixel2));
+    
+    // Verify JSON format
+    Assert.That(json, Does.Contain("\"Symbol\""));
+    Assert.That(json, Does.Contain("\"Color\""));
 }
+
+[Test]
+public void JsonSerializationEdgeCases()
+{
+    // Test caret pixel
+    var caretPixel = new Pixel(true);
+    string caretJson = JsonConvert.SerializeObject(caretPixel);
+    var deserializedCaret = JsonConvert.DeserializeObject<Pixel>(caretJson);
+    Assert.That(caretPixel.Equals(deserializedCaret));
+    
+    // Test transparent background
+    var transparentPixel = new Pixel(new PixelForeground(new SimpleSymbol('a')),
+        new PixelBackground(Colors.Transparent));
+    string transparentJson = JsonConvert.SerializeObject(transparentPixel);
+    var deserializedTransparent = JsonConvert.DeserializeObject<Pixel>(transparentJson);
+    Assert.That(transparentPixel.Equals(deserializedTransparent));
+}
src/Consolonia.Core/Drawing/RenderTarget.cs (1)

197-202: LGTM: Improved handling of complex glyphs

Good improvement for handling double-wide characters correctly. The comment clearly explains the purpose of the width check.

Consider adding unit tests specifically for double-wide character scenarios to ensure this behavior is maintained.

src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs (1)

171-199: Enhance JSON serialization test coverage

While the current tests cover basic scenarios, consider adding:

  1. Negative test cases (invalid JSON)
  2. Validation of the actual JSON structure
  3. Edge cases (null values, special characters)

Example additions:

+        [Test]
+        public void JsonSerialization_InvalidJson_ThrowsException()
+        {
+            const string invalidJson = "{invalid}";
+            Assert.Throws<JsonReaderException>(() => 
+                JsonConvert.DeserializeObject<PixelForeground>(invalidJson));
+        }
+
+        [Test]
+        public void JsonSerialization_ValidatesJsonStructure()
+        {
+            var pixelForeground = new PixelForeground(
+                new SimpleSymbol('a'), 
+                Colors.Red, 
+                FontWeight.Bold
+            );
+            string json = JsonConvert.SerializeObject(pixelForeground);
+            
+            // Verify JSON structure
+            var jsonObj = JsonConvert.DeserializeObject<dynamic>(json);
+            Assert.That(jsonObj.Symbol, Is.Not.Null);
+            Assert.That(jsonObj.Color, Is.Not.Null);
+            Assert.That(jsonObj.Weight, Is.Not.Null);
+        }
src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (5)

13-13: Document the reason for suppressing CA1305.

Add a comment explaining why the IFormatProvider warning is suppressed.

+// Suppressing CA1305 as we're using culture-independent string operations in tests
 #pragma warning disable CA1305 // Specify IFormatProvider

17-19: Consider making ContextApp internal.

Since this is a test-only class and not meant to be used outside this assembly, it should be marked as internal.

-    public class ContextApp : Application
+    internal class ContextApp : Application
     {
     }

60-60: Extract magic number to a constant.

The value 10 is used repeatedly across multiple tests. Consider extracting it to a constant to improve maintainability.

+    private const int TEST_BUFFER_WIDTH = 10;
+
     [Test]
     public void DrawText()
     {
         var consoleWindow = new ConsoleWindow();
         PixelBuffer buffer = consoleWindow.PixelBuffer;
         var dc = new DrawingContextImpl(consoleWindow);

-        for (ushort x = 0; x < 10; x++) DrawText(dc, x, 0, x.ToString(), Brushes.White);
+        for (ushort x = 0; x < TEST_BUFFER_WIDTH; x++) DrawText(dc, x, 0, x.ToString(), Brushes.White);

Also applies to: 75-75, 98-98


250-250: Remove extra blank line.

Remove the unnecessary blank line to maintain consistent spacing.


487-492: Remove commented-out code.

Remove the commented-out GlyphRun instantiation if it's no longer needed. If it's kept for reference, add a comment explaining why.

src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

537-590: Consider refactoring the pixel manipulation logic

The current implementation contains complex nested conditions and repeated pixel manipulation code. Consider extracting the pixel manipulation logic into separate methods for better maintainability.

Consider refactoring like this:

- // if we are attempting to draw a wide glyph we need to make sure that the clipping point
- // is for the last physical char. Aka a double char should be clipped if it's second rendered 
- // char would break the boundary of the clip.
- // var clippingPoint = new Point(characterPoint.X + symbol.Width - 1, characterPoint.Y);
- var newPixel = new Pixel(symbol, foregroundColor, typeface.Style, typeface.Weight);
- CurrentClip.ExecuteWithClipping(characterPoint, () =>
- {
-     _pixelBuffer.Set((PixelBufferCoordinate)characterPoint,
-         oldPixel =>
-         {
-             if (oldPixel.Width == 0)
-             {
-                 // if the oldPixel was empty, we need to set the previous pixel to space
-                 double targetX = characterPoint.X - 1;
-                 if (targetX >= 0)
-                     _pixelBuffer.Set(
-                         (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
-                         oldPixel2 =>
-                             new Pixel(
-                                 new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
-                                 oldPixel2.Background));
-             }
-             else if (oldPixel.Width > 1)
-             {
-                 // if oldPixel was wide we need to reset overlapped symbols from empty to space
-                 for (ushort i = 1; i < oldPixel.Width; i++)
-                 {
-                     double targetX = characterPoint.X + i;
-                     if (targetX < _pixelBuffer.Size.Width)
-                         _pixelBuffer.Set(
-                             (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
-                             oldPixel2 =>
-                                 new Pixel(
-                                     new PixelForeground(new SimpleSymbol(' '),
-                                         Colors.Transparent), oldPixel2.Background));
-                 }
-             }
-
-             // if the pixel was a wide character, we need to set the overlapped pixels to empty pixels.
-             if (newPixel.Width > 1)
-                 for (int i = 1; i < symbol.Width; i++)
-                 {
-                     double targetX = characterPoint.X + i;
-                     if (targetX < _pixelBuffer.Size.Width)
-                         _pixelBuffer.Set(
-                             (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
-                             oldPixel2 =>
-                                 new Pixel(
-                                     new PixelForeground(new SimpleSymbol(), Colors.Transparent),
-                                     oldPixel2.Background));
-                 }
-
-             return oldPixel.Blend(newPixel);
-         });
- });
+ private void HandleEmptyPixel(Point characterPoint)
+ {
+     double targetX = characterPoint.X - 1;
+     if (targetX >= 0)
+         _pixelBuffer.Set(
+             (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
+             oldPixel2 => new Pixel(
+                 new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
+                 oldPixel2.Background));
+ }
+ 
+ private void ResetOverlappedSymbols(Point characterPoint, ushort width)
+ {
+     for (ushort i = 1; i < width; i++)
+     {
+         double targetX = characterPoint.X + i;
+         if (targetX < _pixelBuffer.Size.Width)
+             _pixelBuffer.Set(
+                 (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
+                 oldPixel2 => new Pixel(
+                     new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
+                     oldPixel2.Background));
+     }
+ }
+ 
+ private void SetEmptyOverlappedPixels(Point characterPoint, int width)
+ {
+     for (int i = 1; i < width; i++)
+     {
+         double targetX = characterPoint.X + i;
+         if (targetX < _pixelBuffer.Size.Width)
+             _pixelBuffer.Set(
+                 (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
+                 oldPixel2 => new Pixel(
+                     new PixelForeground(new SimpleSymbol(), Colors.Transparent),
+                     oldPixel2.Background));
+     }
+ }
+ 
+ var newPixel = new Pixel(symbol, foregroundColor, typeface.Style, typeface.Weight);
+ CurrentClip.ExecuteWithClipping(characterPoint, () =>
+ {
+     _pixelBuffer.Set((PixelBufferCoordinate)characterPoint,
+         oldPixel =>
+         {
+             if (oldPixel.Width == 0)
+                 HandleEmptyPixel(characterPoint);
+             else if (oldPixel.Width > 1)
+                 ResetOverlappedSymbols(characterPoint, oldPixel.Width);
+ 
+             if (newPixel.Width > 1)
+                 SetEmptyOverlappedPixels(characterPoint, symbol.Width);
+ 
+             return oldPixel.Blend(newPixel);
+         });
+ });
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs (2)

21-31: Optimize the nested loop for better readability and performance

Currently, the nested loops lack braces, which can affect readability. Additionally, consider using a single loop since you're iterating over the pixel array sequentially.

Apply this diff for improved code:

- for (ushort y = 0; y < height; y++)
- for (ushort x = 0; x < width; x++)
+ for (int index = 0; index < pixels.Count(); index++)
{
-     JToken pixelRecord = pixels[i++];
+     JToken pixelRecord = pixels[index];
      ArgumentNullException.ThrowIfNull(pixelRecord);
      JsonReader rdr = pixelRecord.CreateReader()!;
      ArgumentNullException.ThrowIfNull(rdr);
      var pixel = serializer.Deserialize<Pixel>(rdr);
      ArgumentNullException.ThrowIfNull(pixel);
-     pixelBuffer[x, y] = pixel;
+     pixelBuffer[index % width, index / width] = pixel;
}

26-27: Remove redundant null-check after non-nullable method

The CreateReader() method does not return null. The subsequent null-check using ArgumentNullException.ThrowIfNull(rdr); is unnecessary and can be removed to streamline the code.

Apply this diff to remove the redundant null-check:

JsonReader rdr = pixelRecord.CreateReader()!;
- ArgumentNullException.ThrowIfNull(rdr);
var pixel = serializer.Deserialize<Pixel>(rdr);
src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs (2)

74-74: Make the font name configurable instead of hardcoding "Cascadia Mono"

Hardcoding the font name limits flexibility and may cause issues if the font isn't available on the system. Consider introducing a property to set the font family or using a fallback mechanism to select an available monospace font.


71-83: Dispose of previous GlyphRun to prevent memory leaks

Each time the Text property is set, a new GlyphRun is created. To avoid memory leaks, dispose of the existing _shapedText before assigning a new one.

Add the disposal before creating the new GlyphRun:

set
{
    _text = value;

+   _shapedText?.Dispose();

    var platformRender = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
    // ...
src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (5)

27-31: Simplify null checks in Equals method

You can simplify the null check by using if (other == null) instead of (object)other is null. Additionally, since UpRightDownLeft is non-nullable, you can safely access other.UpRightDownLeft without the null-forgiving operator.

Apply this diff to simplify the code:

public bool Equals(DrawingBoxSymbol other)
{
-    if ((object)other is null) return false;
-    return UpRightDownLeft == other!.UpRightDownLeft;
+    if (other == null) return false;
+    return UpRightDownLeft == other.UpRightDownLeft;
}

33-33: Confirm the need for public init accessor on Text property

By changing the Text property from private init to init, it becomes publicly settable during object initialization, even though [JsonIgnore] is applied. If external initialization of Text is not required, consider keeping the private init accessor for encapsulation. Otherwise, ensure this change aligns with the desired usage.


42-52: Review the use of ref keyword in Blend method parameter

The Blend method accepts symbolAbove as a ref parameter, but there is no modification to symbolAbove within the method. Consider removing the ref keyword if passing by reference is not necessary.

Apply this diff to remove the unnecessary use of ref:

-public ISymbol Blend(ref ISymbol symbolAbove)
+public ISymbol Blend(ISymbol symbolAbove)
{
    if (symbolAbove.IsWhiteSpace()) return this;

    if (symbolAbove is not DrawingBoxSymbol drawingBoxSymbol)
        return symbolAbove;

    if (drawingBoxSymbol.UpRightDownLeft == BoldSymbol || UpRightDownLeft == BoldSymbol)
        return new DrawingBoxSymbol(BoldSymbol);

    return new DrawingBoxSymbol((byte)(UpRightDownLeft | drawingBoxSymbol.UpRightDownLeft));
}

158-161: Simplify null checks in equality operator ==

Simplify the null check by using if (left == null) instead of (object)left is null for better readability.

Apply this diff:

public static bool operator ==(DrawingBoxSymbol left, DrawingBoxSymbol right)
{
-    if ((object)left is null) return (object)right is null;
+    if (left == null) return right == null;
    return left.Equals(right);
}

164-165: Simplify null checks in equality operator !=

Simplify the null check using if (left == null), and remove the unnecessary null-forgiving operator.

Apply this diff:

public static bool operator !=(DrawingBoxSymbol left, DrawingBoxSymbol right)
{
-    if ((object)left is null) return (object)right is not null;
-    return !left!.Equals(right);
+    if (left == null) return right != null;
+    return !left.Equals(right);
}
src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (3)

91-92: Use pattern matching for null checks

Consider using pattern matching with is null instead of casting to (object) for null checks to improve readability and adhere to modern C# conventions.

Apply this diff:

-if ((object)other == null)
+if (other is null)

182-183: Modernize null checks in equality operator

Replace the null checks using (object) casting with pattern matching is null for consistency and clarity.

Apply this diff:

-if ((object)left is null) return (object)right is null;
+if (left is null) return right is null;

189-190: Modernize null checks in inequality operator

Update the null checks to use is null pattern matching for improved readability.

Apply this diff:

-if ((object)left is null) return (object)right is not null;
+if (left is null) return right is not null;
src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (2)

85-94: Avoid silently suppressing exceptions in SetTitle

While setting the console title might fail in certain environments, silently ignoring all exceptions can make debugging difficult.

Consider:

  • Catching specific exceptions: Handle known exceptions that are expected in certain environments.
  • Logging warnings: Provide a log entry or debug message when an exception is caught to aid in troubleshooting.

Line range hint 119-149: Ensure correct handling of multiple text decorations in Print method

Currently, the method checks for Underline and Strikethrough separately but does not support combining them. If TextDecorationLocation can represent multiple decorations, the current logic won't apply both styles simultaneously.

Consider updating the code to handle multiple text decorations:

 if (textDecoration.HasValue)
 {
-    if (textDecoration == TextDecorationLocation.Underline)
+    if (textDecoration.Value.HasFlag(TextDecorationLocation.Underline))
         sb.Append(Esc.Underline);
-    if (textDecoration == TextDecorationLocation.Strikethrough)
+    if (textDecoration.Value.HasFlag(TextDecorationLocation.Strikethrough))
         sb.Append(Esc.Strikethrough);
 }
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (4)

43-44: Consider making PixelBuffer property read-only

The PixelBuffer property is currently defined with both get and set accessors:

public PixelBuffer PixelBuffer { get; set; }

If external modification of PixelBuffer is not intended, consider making the setter private or protected to encapsulate the property and prevent unintended side effects from external assignments.


255-260: Remove commented-out finalizer

The commented-out finalizer is unnecessary and can be removed to clean up the code:

// ~ConsoleWindow()
// {
//     Dispose(disposing: false);
// }

Removing dead code improves readability and maintains a clean codebase.


315-317: Use predefined methods instead of hardcoded escape sequences

The code uses a hardcoded escape sequence to clear the console screen:

#pragma warning disable CA1303 // Do not pass literals as localized parameters
System.Console.Write(Esc.ClearScreen);
#pragma warning restore CA1303 // Do not pass literals as localized parameters

Consider using built-in methods like Console.Clear() or defining constants in a centralized location to improve readability and maintainability.


21-21: Address static analysis warning about virtual members

Static analysis has raised a warning:

"[ClassWithVirtualMembersNeverInherited.Global] Class 'ConsoleWindow' has some virtual members but no inheritors"

Since ConsoleWindow has virtual members but currently no derived classes, consider:

  • Sealing the class if inheritance is not intended:

    public sealed class ConsoleWindow : IWindowImpl
  • Keeping the class as is, if future inheritance is planned.

  • Removing the virtual modifiers from members if they are not meant to be overridden.

🧰 Tools
🪛 GitHub Check: build

[notice] 21-21:
"[ClassWithVirtualMembersNeverInherited.Global] Class 'ConsoleWindow' has some virtual members but no inheritors" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs(21,18)

src/Consolonia.Designer/ConsolePreview.cs (2)

280-301: Add exception handling for potential I/O errors in FindFile method

The FindFile method uses Directory.GetFiles, which can throw exceptions such as UnauthorizedAccessException or DirectoryNotFoundException if there are permission issues or if a directory does not exist.

Consider adding exception handling around file system operations to handle potential errors gracefully.

Apply this diff to improve robustness:

while (currentFolder != null)
{
    try
    {
        // Check if any .csproj or .sln file exists in the current directory
        if (Directory.GetFiles(currentFolder, "*.csproj").Length > 0 ||
            Directory.GetFiles(currentFolder, "*.sln").Length > 0)
        {
            // Search for the specified file in the current directory and its subdirectories
            string[] files = Directory.GetFiles(currentFolder, fileName, SearchOption.AllDirectories);
            if (files.Length > 0) return files[0]; // Return the first match
        }
    }
+   catch (Exception ex) when (ex is UnauthorizedAccessException || ex is DirectoryNotFoundException)
+   {
+       // Handle or log the exception as needed
+       Debug.WriteLine($"Access denied or directory not found: {ex.Message}");
+   }

    // Move up to the parent directory
    currentFolder = Directory.GetParent(currentFolder)?.FullName;
}

343-426: Consider moving TextBlockComposer class to a separate file

The TextBlockComposer class is nested within the ConsolePreview class, which can reduce readability and maintainability.

Consider moving TextBlockComposer to its own file or making it a private nested class if it's only used internally.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between eb7c2da and bf5f0a8.

⛔ Files ignored due to path filters (2)
  • Icon.png is excluded by !**/*.png
  • src/Tools/Avalonia.PreviewHost/Assets/consolonia-logo.ico is excluded by !**/*.ico
📒 Files selected for processing (82)
  • src/Consolonia.Core/Assembly.cs (1 hunks)
  • src/Consolonia.Core/Consolonia.Core.csproj (1 hunks)
  • src/Consolonia.Core/Drawing/DrawingContextImpl.cs (8 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/ColorConverter.cs (1 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (2 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs (1 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (7 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (3 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs (2 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs (1 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (3 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (3 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs (1 hunks)
  • src/Consolonia.Core/Drawing/RenderTarget.cs (11 hunks)
  • src/Consolonia.Core/Dummy/DummyConsole.cs (1 hunks)
  • src/Consolonia.Core/Helpers/Extensions.cs (2 hunks)
  • src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (6 hunks)
  • src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs (2 hunks)
  • src/Consolonia.Core/Infrastructure/IConsole.cs (1 hunks)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (8 hunks)
  • src/Consolonia.Core/Properties/launchSettings.json (1 hunks)
  • src/Consolonia.Core/Text/Esc.cs (2 hunks)
  • src/Consolonia.Designer/Assembly.cs (1 hunks)
  • src/Consolonia.Designer/ConsolePreview.cs (1 hunks)
  • src/Consolonia.Designer/Consolonia.Designer.csproj (1 hunks)
  • src/Consolonia.Designer/Extensions.cs (1 hunks)
  • src/Consolonia.Gallery/App.cs (2 hunks)
  • src/Consolonia.Gallery/Consolonia.Gallery.csproj (1 hunks)
  • src/Consolonia.Gallery/Consolonia.Gallery.sln (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryAnimatedLines.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryButton.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCalendar.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCalendarPicker.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCanvas.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCheckBox.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryColors.axaml (2 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryComboBox.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryDataGrid.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryDialog.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryFlyout.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryGradientBrush.axaml (2 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryImage.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryListBox.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryMenu.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryProgressBar.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryRadioButton.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryScrollViewer.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTabControl.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTextBlock.axaml (3 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTextBox.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml.cs (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/ResourceTest.axaml (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml (1 hunks)
  • src/Consolonia.Gallery/Program.cs (1 hunks)
  • src/Consolonia.Gallery/View/ControlsListView.axaml (1 hunks)
  • src/Consolonia.Gallery/View/ControlsListView.axaml.cs (1 hunks)
  • src/Consolonia.NUnit/UnitTestConsole.cs (3 hunks)
  • src/Consolonia.PlatformSupport/PlatformSupportExtensions.cs (2 hunks)
  • src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs (2 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/Fluent/Fluent.axaml (1 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/Fluent/FluentColors.axaml (1 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/Fluent/FluentTheme.cs (1 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/Material/Material.axaml (1 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml (1 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/Material/MaterialTheme.cs (1 hunks)
  • src/Consolonia.Themes.TurboVision/Themes/TurboVisionBlack/TurboVisionBlack.axaml (1 hunks)
  • src/Consolonia.sln (3 hunks)
  • src/Tests/Consolonia.Core.Tests/Consolonia.Core.Tests.csproj (1 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingBoxSymbolTests.cs (2 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs (4 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelBufferTests.cs (1 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs (9 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelTests.cs (5 hunks)
  • src/Tests/Consolonia.Core.Tests/SimpleSymbolTests.cs (2 hunks)
  • src/Tools/Avalonia.PreviewHost/App.axaml (1 hunks)
  • src/Tools/Avalonia.PreviewHost/App.axaml.cs (1 hunks)
  • src/Tools/Avalonia.PreviewHost/Assembly.cs (1 hunks)
  • src/Tools/Avalonia.PreviewHost/Avalonia.PreviewHost.csproj (1 hunks)
  • src/Tools/Avalonia.PreviewHost/Program.cs (1 hunks)
  • src/Tools/Avalonia.PreviewHost/ViewModels/FileViewModel.cs (1 hunks)
⛔ Files not processed due to max files limit (19)
  • src/Tools/Avalonia.PreviewHost/ViewModels/MainViewModel.cs
  • src/Tools/Avalonia.PreviewHost/Views/MainWindow.axaml
  • src/Tools/Avalonia.PreviewHost/Views/MainWindow.axaml.cs
  • src/Tools/Avalonia.PreviewHost/app.manifest
  • src/Tools/Consolonia.PreviewHost/App.axaml
  • src/Tools/Consolonia.PreviewHost/App.axaml.cs
  • src/Tools/Consolonia.PreviewHost/Assembly.cs
  • src/Tools/Consolonia.PreviewHost/Consolonia.PreviewHost.csproj
  • src/Tools/Consolonia.PreviewHost/CustomAssemblyLoadContext.cs
  • src/Tools/Consolonia.PreviewHost/Program.cs
  • src/Tools/Consolonia.PreviewHost/Properties/launchSettings.json
  • src/Tools/Consolonia.PreviewHost/ViewModels/AppViewModel.cs
  • src/Tools/Consolonia.PreviewHost/ViewModels/ProjectViewModel.cs
  • src/Tools/Consolonia.PreviewHost/ViewModels/XamlFileViewModel.cs
  • src/Tools/Consolonia.PreviewHost/Views/HeadlessWindow.axaml
  • src/Tools/Consolonia.PreviewHost/Views/HeadlessWindow.axaml.cs
  • src/Tools/Consolonia.PreviewHost/Views/MainWindow.axaml
  • src/Tools/Consolonia.PreviewHost/Views/MainWindow.axaml.cs
  • src/Tools/Consolonia.PreviewHost/readme.md
✅ Files skipped from review due to trivial changes (23)
  • src/Consolonia.Core/Properties/launchSettings.json
  • src/Consolonia.Designer/Assembly.cs
  • src/Consolonia.Gallery/Consolonia.Gallery.sln
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryAnimatedLines.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryButton.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCalendar.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCanvas.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCheckBox.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryDataGrid.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryDialog.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryFlyout.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryImage.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryListBox.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryMenu.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryProgressBar.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryRadioButton.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryScrollViewer.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTextBox.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml.cs
  • src/Consolonia.Gallery/Gallery/GalleryViews/ResourceTest.axaml
  • src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml
  • src/Tools/Avalonia.PreviewHost/App.axaml
  • src/Tools/Avalonia.PreviewHost/Assembly.cs
🧰 Additional context used
📓 Learnings (2)
src/Consolonia.Themes.TurboVision/Themes/Fluent/FluentColors.axaml (1)
Learnt from: tomlm
PR: jinek/Consolonia#122
File: src/Consolonia.Themes.TurboVision/Themes/TurboVisionDark/TurboVisionDarkColors.axaml:5-28
Timestamp: 2024-11-12T04:11:38.190Z
Learning: In the project, using `Mode="Shaded"` in `ConsoleBrush` within theme files is intentional and does not cause runtime issues.
src/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml (1)
Learnt from: tomlm
PR: jinek/Consolonia#122
File: src/Consolonia.Themes.TurboVision/Themes/TurboVisionDark/TurboVisionDarkColors.axaml:5-28
Timestamp: 2024-11-12T04:11:38.190Z
Learning: In the project, using `Mode="Shaded"` in `ConsoleBrush` within theme files is intentional and does not cause runtime issues.
🪛 GitHub Check: build
src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs

[notice] 85-85:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs(85,36)

src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs

[notice] 49-49:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs(49,49)

src/Consolonia.Core/Dummy/DummyConsole.cs

[notice] 27-27:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Dummy/DummyConsole.cs(27,44)


[notice] 12-12:
"[ClassWithVirtualMembersNeverInherited.Global] Class 'DummyConsole' has some virtual members but no inheritors" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Dummy/DummyConsole.cs(12,18)

src/Consolonia.Core/Infrastructure/ConsoleWindow.cs

[notice] 21-21:
"[ClassWithVirtualMembersNeverInherited.Global] Class 'ConsoleWindow' has some virtual members but no inheritors" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs(21,18)

src/Consolonia.Designer/ConsolePreview.cs

[notice] 30-30:
"[ClassWithVirtualMembersNeverInherited.Global] Class 'ConsolePreview' has some virtual members but no inheritors" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Designer/ConsolePreview.cs(30,18)

src/Tools/Avalonia.PreviewHost/Program.cs

[notice] 5-5:
"[ClassNeverInstantiated.Global] Class 'Program' is never instantiated" on /home/runner/work/Consolonia/Consolonia/src/Tools/Avalonia.PreviewHost/Program.cs(5,20)

🔇 Additional comments (126)
src/Consolonia.Core/Assembly.cs (1)

5-5: LGTM! Verify assembly name.

The addition of InternalsVisibleTo for the designer assembly aligns with the PR objectives to support design-time preview functionality.

Let's verify the exact assembly name:

✅ Verification successful

Assembly name verification successful

The InternalsVisibleTo attribute correctly references "Consolonia.Designer", which matches the project structure. The Designer project exists at src/Consolonia.Designer and follows the default .NET SDK naming convention where the assembly name matches the project name when no explicit AssemblyName is specified.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the assembly name matches the project output
# Expected: Find a .csproj file that produces "Consolonia.Designer" assembly

# Search for relevant project file
fd -e csproj | xargs grep -l "Consolonia.Designer" || echo "Project file not found"

Length of output: 158


Script:

#!/bin/bash
# Let's check the content of these project files to verify assembly names
echo "=== Checking Consolonia.Gallery.csproj ==="
cat "src/Consolonia.Gallery/Consolonia.Gallery.csproj"

echo -e "\n=== Checking Avalonia.PreviewHost.csproj ==="
cat "src/Tools/Avalonia.PreviewHost/Avalonia.PreviewHost.csproj"

# Also search for any other files that might contain Designer assembly references
echo -e "\n=== Searching for Designer assembly references ==="
rg -i "Consolonia\.Designer" --type cs

Length of output: 3382


Script:

#!/bin/bash
# Let's verify the Designer project's assembly name
echo "=== Checking Consolonia.Designer.csproj ==="
cat "src/Consolonia.Designer/Consolonia.Designer.csproj"

# Also check if there are any AssemblyName overrides
rg -i "AssemblyName" "src/Consolonia.Designer/Consolonia.Designer.csproj" || true

Length of output: 1185

src/Tools/Avalonia.PreviewHost/ViewModels/FileViewModel.cs (2)

1-4: LGTM! Clean and focused imports.

The namespace and import are well-structured and appropriate for the MVVM pattern implementation.


5-6: LGTM! Well-structured MVVM implementation.

The class is correctly set up with MVVM Toolkit's ObservableObject base class and partial modifier for source generation.

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml (2)

3-5: LGTM! Design-time support properly configured.

The addition of design-time namespaces and markup compatibility settings is essential for enabling the live preview functionality. The configuration follows XAML best practices.


7-7: LGTM! Clean and consistent formatting.

The TextBlock element has been reformatted for better readability while maintaining the same functionality.

src/Consolonia.Themes.TurboVision/Themes/Fluent/FluentTheme.cs (4)

1-6: LGTM! Clean and well-organized imports and namespace.

The imports are minimal and the namespace follows proper hierarchical structure.


10-16: Verify the necessity of both constructors.

Both constructors delegate to the base class. Let's verify if both constructor overloads are actually needed.

#!/bin/bash
# Description: Check usage patterns of FluentTheme constructors

# Search for FluentTheme instantiations
ast-grep --pattern 'new FluentTheme($$$)'

# Search for dependency injection registrations
rg "AddSingleton.*FluentTheme|AddTransient.*FluentTheme|AddScoped.*FluentTheme" --type cs

18-19: Verify resource file existence and consider adding documentation.

The URI points to a Fluent.axaml resource file. Let's verify its existence and suggest adding XML documentation to explain the theme's purpose.

#!/bin/bash
# Description: Verify the existence of Fluent.axaml and its content

# Check if the file exists
fd "Fluent.axaml$"

# If found, check its content structure
fd "Fluent.axaml$" --exec cat {} \; || echo "File not found"

Consider adding XML documentation to explain the theme's purpose and styling approach:

     [SuppressMessage("ReSharper", "UnusedMember.Global")]
+    /// <summary>
+    /// Provides a Fluent design system theme for Consolonia applications.
+    /// </summary>
     public class FluentTheme : ResourceIncludeBase

7-8: Verify theme registration and usage.

The SuppressMessage attribute suggests this theme class is loaded dynamically. Let's verify its registration and usage in the theme system.

✅ Verification successful

FluentTheme is correctly used and registered

The verification shows that FluentTheme is a legitimate part of the theming system:

  • It's used in src/Tools/Avalonia.PreviewHost/App.axaml as <FluentTheme />
  • It follows the same pattern as other themes (MaterialTheme, TurboVisionBlackTheme, etc.)
  • The base class ResourceIncludeBase is specifically designed for theme implementation, being a "Copy-paste from FluentTheme from Avalonia"
  • It provides two constructors for different initialization scenarios (Uri and IServiceProvider based)

The SuppressMessage attribute is justified as the class is used through XAML/dependency injection rather than direct code references.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how FluentTheme is registered and used

# Check for theme registration
rg -l "FluentTheme" --type cs

# Check for resource references to Fluent.axaml
rg "Fluent\.axaml" --type xml

Length of output: 817


Script:

#!/bin/bash
# Let's try a different approach to verify the usage

# Check for FluentTheme class references
rg "FluentTheme" -A 3 -B 3

# Check for any theme registration patterns
ast-grep --pattern 'new FluentTheme()'

# Look for theme registration in startup/configuration files
fd -e cs -e json --exec grep -l "Theme" {} \;

# Check if there are any other theme classes following similar pattern
ast-grep --pattern 'class $_ : ResourceIncludeBase'

Length of output: 4571

src/Consolonia.Themes.TurboVision/Themes/Fluent/Fluent.axaml (3)

3-5: Verify if empty preview section is intentional

Given that this PR implements external preview functionality via Consolonia.PreviewHost, is this empty Design.PreviewWith section intentional? If not needed, consider removing it to maintain cleaner markup.


1-15: LGTM! The structure follows Avalonia UI conventions

The file structure is well-organized and follows Avalonia UI conventions:

  1. Proper XML namespace declarations
  2. Resource dictionary with merged color resources
  3. Style includes for base templates and specific controls

9-10: Verify referenced AXAML files exist

The theme references several external AXAML files. Let's verify they exist in the correct locations:

  • /Themes/Material/MaterialColors.axaml
  • /Templates/TurboVision.axaml
  • /Themes/TurboVisionBlack/Controls/Button.axaml

Also applies to: 13-14

✅ Verification successful

All referenced AXAML files exist in the correct locations

The verification confirms that all referenced AXAML files exist in their expected locations:

  • /Themes/Material/MaterialColors.axamlsrc/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml
  • /Templates/TurboVision.axamlsrc/Consolonia.Themes.TurboVision/Templates/TurboVision.axaml
  • /Themes/TurboVisionBlack/Controls/Button.axamlsrc/Consolonia.Themes.TurboVision/Themes/TurboVisionBlack/Controls/Button.axaml
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of referenced AXAML files

# Search for the referenced files
echo "Checking for MaterialColors.axaml..."
fd "MaterialColors.axaml$"

echo "Checking for TurboVision.axaml..."
fd "TurboVision.axaml$"

echo "Checking for Button.axaml in TurboVisionBlack theme..."
fd "Button.axaml$" | grep "TurboVisionBlack"

Length of output: 591

src/Consolonia.Themes.TurboVision/Themes/Material/Material.axaml (2)

13-14: Review the mixing of TurboVision and Material styles.

The file includes both TurboVision and Material theme styles. This mixing of styles needs careful consideration:

  1. Are TurboVision styles meant to be the base styles for Material theme?
  2. Could this cause any style conflicts?
#!/bin/bash
# Description: Check the structure of referenced style files to understand the inheritance
# Expected: Understanding the base styles and potential conflicts

echo "Checking TurboVision base template..."
rg -A 10 "Style|Template|Resource" src/Consolonia.Themes.TurboVision/Templates/TurboVision.axaml

echo "Checking Button styles..."
rg -A 10 "Style|Template|Resource" src/Consolonia.Themes.TurboVision/Themes/TurboVisionBlack/Controls/Button.axaml

Consider documenting the style inheritance hierarchy and ensuring that Material theme properly overrides any conflicting TurboVision styles. This will help maintain a consistent Material Design look and feel.


9-9: Verify the MaterialColors.axaml resource path.

Let's ensure the referenced MaterialColors.axaml file exists at the specified location.

✅ Verification successful

Let me try another search strategy to verify the existence of MaterialColors.axaml.


MaterialColors.axaml exists at the correct location

The file exists exactly where it's being referenced: src/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml. The resource path in the Material.axaml file is correct and valid.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of MaterialColors.axaml
# Expected: File should exist in the same directory as Material.axaml

fd --type f "MaterialColors.axaml" src/Consolonia.Themes.TurboVision/Themes/Material

Length of output: 1291


Script:

#!/bin/bash
# Let's try a broader search for the file
fd MaterialColors.axaml

# Also check if there are any similar files that might have been renamed
fd -e axaml -e xaml | grep -i material

Length of output: 273

src/Consolonia.Themes.TurboVision/Themes/Material/MaterialTheme.cs (2)

1-6: LGTM: Imports and namespace structure are well-organized.

The imports are minimal and necessary, and the namespace hierarchy properly reflects the component's location in the project structure.


18-19: Verify Material.axaml resource file existence and content.

The Uri points to a Material theme AXAML file. Let's verify its existence and content.

✅ Verification successful

Material theme resource file and its dependencies are properly configured

The verification confirms:

  • Material.axaml exists at the correct location
  • It includes the required style files:
    • TurboVision.axaml (verified)
    • Button.axaml (verified)
  • Both referenced files contain valid Avalonia style definitions
  • The resource chain is properly structured with ResourceDictionary and StyleIncludes
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify Material theme resource file

# Check if the Material.axaml file exists
echo "Checking Material.axaml existence..."
fd "Material.axaml" src/Consolonia.Themes.TurboVision/Themes/Material/

# Check Material.axaml content for style definitions
echo "Checking Material theme content..."
if [ -f "src/Consolonia.Themes.TurboVision/Themes/Material/Material.axaml" ]; then
    rg "Style|ResourceDictionary|StaticResource" src/Consolonia.Themes.TurboVision/Themes/Material/Material.axaml
fi

Length of output: 890


Script:

#!/bin/bash
# Let's verify the referenced style includes exist

echo "Checking referenced style includes..."
fd "TurboVision.axaml" src/Consolonia.Themes.TurboVision/Templates/
fd "Button.axaml" src/Consolonia.Themes.TurboVision/Themes/TurboVisionBlack/Controls/

# Check if these files contain style definitions
echo -e "\nChecking content of referenced files..."
if [ -f "src/Consolonia.Themes.TurboVision/Templates/TurboVision.axaml" ]; then
    echo -e "\nTurboVision.axaml content:"
    rg "Style|ResourceDictionary|StaticResource" src/Consolonia.Themes.TurboVision/Templates/TurboVision.axaml
fi

if [ -f "src/Consolonia.Themes.TurboVision/Themes/TurboVisionBlack/Controls/Button.axaml" ]; then
    echo -e "\nButton.axaml content:"
    rg "Style|ResourceDictionary|StaticResource" src/Consolonia.Themes.TurboVision/Themes/TurboVisionBlack/Controls/Button.axaml
fi

Length of output: 1533

src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs (2)

1-1: LGTM! Clean implementation of JSON serialization support

The changes follow best practices for implementing custom JSON serialization:

  • Using the industry-standard Newtonsoft.Json library
  • Properly attributing the interface for custom serialization
  • No breaking changes to the interface contract

Also applies to: 5-5


5-5: Verify SymbolConverter implementation exists

The JsonConverter attribute references SymbolConverter. Let's verify its implementation.

✅ Verification successful

SymbolConverter implementation is correct and functional

The SymbolConverter class is properly implemented:

  • Inherits from JsonConverter
  • Implements required methods (CanConvert, ReadJson)
  • Handles both string-based SimpleSymbol and numeric DrawingBoxSymbol conversions
  • Correctly validates type compatibility with ISymbol interface
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify SymbolConverter exists and implements JsonConverter<ISymbol>

# Search for SymbolConverter class definition
echo "Searching for SymbolConverter implementation..."
ast-grep --pattern 'class SymbolConverter : JsonConverter<ISymbol>
{
  $$$
}'

# Backup: Search for any file containing SymbolConverter
echo "Searching for files containing SymbolConverter..."
rg -l "SymbolConverter"

Length of output: 599


Script:

#!/bin/bash
# Let's check the implementation of SymbolConverter
echo "=== SymbolConverter Implementation ==="
rg -A 20 "class SymbolConverter" src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs

# Also check if it's properly implementing JsonConverter
echo -e "\n=== JsonConverter base class reference ==="
rg "JsonConverter" src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs

Length of output: 1155

src/Consolonia.Core/Consolonia.Core.csproj (2)

6-6: Verify Linux compatibility after removing FreeDesktop dependency.

The switch to ReactiveUI is a good architectural choice for implementing the live preview functionality. However, since Avalonia.FreeDesktop was removed, we should ensure Linux compatibility is maintained.

✅ Verification successful

Let me analyze the output from the search to verify Linux compatibility after removing the FreeDesktop dependency.

The search results show that the codebase has robust platform-specific handling:

  1. In PlatformSupportExtensions.cs:
IConsole console = Environment.OSVersion.Platform switch
{
    PlatformID.Win32S or PlatformID.Win32Windows or PlatformID.Win32NT => new Win32Console(),
    PlatformID.Unix or PlatformID.MacOSX => new CursesConsole(),
    _ => throw new PlatformNotSupportedException()
};
  1. The CursesConsole class is specifically implemented for Unix/Linux systems, providing terminal input/output functionality through ncurses.

  2. The library loading system in UnmanagedLibrary.cs has specific handling for Linux:

if (IsLinux) {
    return Linux.dlopen (libraryPath, RTLD_GLOBAL + RTLD_LAZY);
}
  1. The removal of Avalonia.FreeDesktop and SkiaSharp.NativeAssets.Linux.NoDependencies is compensated by:
    • Using CursesConsole for terminal handling
    • Platform-agnostic ReactiveUI for MVVM functionality
    • Native ncurses library loading for Linux systems

Linux compatibility is maintained through platform-specific implementations

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any Linux-specific functionality that might be affected

# Search for Linux-specific code or platform checks
rg -i "linux|platform|runtime|os" --type cs

# Look for potential platform-specific issues in issues
gh issue list --label "platform-specific,linux"

Length of output: 72863


11-11: Consider using System.Text.Json and verify package version.

While Newtonsoft.Json is reliable, consider using the more modern System.Text.Json which is built into .NET. Additionally, let's verify the security of the fixed version 13.0.3.

src/Consolonia.Core/Drawing/PixelBufferImplementation/ColorConverter.cs (3)

1-6: LGTM: Imports and namespace are well-structured

The imports cover all necessary dependencies for color handling and JSON serialization.


7-8: LGTM: Class declaration follows JSON.NET conventions

The class properly extends JsonConverter for type-safe color serialization.


1-22: Verify color format compatibility and converter usage

Since this converter is crucial for the design-time preview feature, we should verify:

  1. The color format compatibility between Avalonia and JSON
  2. The proper registration and usage of this converter
✅ Verification successful

Color format compatibility and converter usage verified successfully

The verification shows that:

  1. The ColorConverter is properly integrated and used with [JsonConverter] attribute in both PixelForeground and PixelBackground classes for their Color properties
  2. The color format compatibility is maintained through:
    • Using Avalonia's built-in Color.TryParse for deserialization
    • Using color's ToString() for serialization
    • Proper fallback to Colors.Transparent for invalid values
  3. The implementation supports both named colors (e.g., "White", "DarkBlue") and hex format ("#FFFFFF", "#80757575") as evidenced by the AXAML usage
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for ColorConverter usage and registration
echo "Checking for ColorConverter usage..."
rg -l "ColorConverter" 

echo "Checking for JsonConverter attribute usage with ColorConverter..."
rg "\[JsonConverter.*ColorConverter\]" -l

echo "Checking color format usage in AXAML files..."
fd -e axaml -x rg "Color=" {} \;

Length of output: 3631


Script:

#!/bin/bash
# Check for color-related attributes in PixelForeground and PixelBackground
echo "Checking PixelForeground and PixelBackground implementation..."
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs

# Check for any color parsing or serialization tests
echo -e "\nChecking for color-related tests..."
rg -l "ColorConverter.*Test" 
rg "Color\.Parse|TryParse.*Color" --type cs -C 2

# Check for any JSON serialization configuration
echo -e "\nChecking JSON serialization setup..."
rg "JsonSerializerSettings|AddJsonConverter|ConfigureJson" --type cs -C 2

Length of output: 7367

src/Consolonia.Gallery/Program.cs (3)

5-5: LGTM: Designer namespace import aligns with PR objectives

The addition of the Consolonia.Designer namespace import supports the new design-time preview functionality.


13-13: LGTM: STAThread attribute is required for proper UI threading

The STAThread attribute ensures proper COM threading model, which is essential for UI applications and Windows Forms/WPF interop.


16-18: Verify the removal of command-line argument validation

The previous argument length check has been removed. Please confirm if this validation is handled elsewhere or if it's no longer needed.

src/Tools/Avalonia.PreviewHost/Program.cs (2)

7-15: LGTM! Well-structured entry point with proper initialization

The implementation follows Avalonia's best practices:

  • Correct use of [STAThread] attribute
  • Helpful warning comment about initialization order
  • Proper application startup sequence

17-24: Verify preview host configuration requirements

While the basic Avalonia configuration is present, the PR objectives mention specific requirements for the preview host, such as:

  • Handling file change detection
  • Supporting automatic reloading
  • Configuring preview-specific rendering settings

Please verify if additional configuration is needed in the BuildAvaloniaApp method to support these preview-specific features.

src/Tests/Consolonia.Core.Tests/Consolonia.Core.Tests.csproj (2)

11-11: LGTM: Adding Newtonsoft.Json for serialization tests

The addition of Newtonsoft.Json package is appropriate for implementing JSON serialization tests.


11-11: Verify package version compatibility and security

Let's ensure the Newtonsoft.Json version is consistent across projects and free from vulnerabilities.

✅ Verification successful

Newtonsoft.Json package version is consistent and secure

The verification shows:

  • Version 13.0.3 is used consistently across all projects
  • This version is newer than the last known vulnerable version (13.0.1)
  • No active security vulnerabilities affect version 13.0.3
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check package version consistency and security

# Check if the same version is used in the main project
echo "Checking Newtonsoft.Json version consistency across projects..."
rg -l 'PackageReference.*Newtonsoft.Json' | while read -r file; do
  echo "=== $file ==="
  grep -A 1 'Newtonsoft.Json' "$file"
done

# Check for security advisories
echo -e "\nChecking for security advisories..."
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NUGET, package: "Newtonsoft.Json") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 2405

src/Consolonia.Designer/Consolonia.Designer.csproj (3)

1-3: LGTM! Good use of shared build properties.

The project setup follows best practices by using shared build properties, which helps maintain consistency across the solution.


10-10: Verify Newtonsoft.Json version for security vulnerabilities.

The fixed version 13.0.3 should be verified for known security vulnerabilities.

✅ Verification successful

The GitHub API query for security vulnerabilities didn't return any results, which suggests there are no known security vulnerabilities for Newtonsoft.Json in GitHub's advisory database. Additionally, the version check shows consistent usage of version 13.0.3 across multiple projects in the solution.

Newtonsoft.Json 13.0.3 is secure and consistently used

The version 13.0.3 is:

  • Free from known security vulnerabilities in GitHub's advisory database
  • Consistently used across all projects in the solution:
    • src/Consolonia.Designer/Consolonia.Designer.csproj
    • src/Consolonia.Core/Consolonia.Core.csproj
    • src/Tests/Consolonia.Core.Tests/Consolonia.Core.Tests.csproj
    • src/Tools/Avalonia.PreviewHost/Avalonia.PreviewHost.csproj
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for security advisories for Newtonsoft.Json
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NUGET, package: "Newtonsoft.Json") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

# Check if this version is consistent across the solution
echo "Checking version consistency across solution..."
rg -g "*.csproj" "Include=\"Newtonsoft.Json\"" -A 1

Length of output: 2318


6-9: Verify Avalonia package completeness for preview functionality.

The current Avalonia package references look good, but let's verify we have all necessary packages for the preview functionality described in the PR objectives.

✅ Verification successful

Let me try another search to specifically look for Avalonia package references, as the previous search seems to have focused on project references and Newtonsoft.Json:


Avalonia package references are appropriate for preview functionality

The package references in Consolonia.Designer.csproj are correctly configured for the preview functionality:

  • Core Avalonia packages are included
  • FreeDesktop support is added for Linux environments
  • Debug-specific packages (Desktop and Fonts.Inter) align with preview requirements
  • Version consistency is maintained via $(AvaloniaVersion) variable (11.0.9)

The project has all necessary dependencies when considered alongside the PreviewHost projects which contain complementary packages like Avalonia.Themes.Simple, Avalonia.Markup.Xaml.Loader, and Avalonia.Diagnostics.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Avalonia package references across the solution to ensure consistency
echo "Searching for Avalonia package references in other projects..."
rg -g "*.csproj" "Include=\"Avalonia" -A 1

# Check if we're missing any commonly paired Avalonia packages
echo "Checking for commonly paired Avalonia packages..."
for pkg in "Avalonia.Controls" "Avalonia.Diagnostics" "Avalonia.Themes.Fluent" "Avalonia.Themes.Simple"; do
    rg -g "*.csproj" "Include=\"$pkg\"" || echo "Note: $pkg not found in any project"
done

Length of output: 2318


Script:

#!/bin/bash
# Search specifically for Avalonia package references
echo "Searching for all Avalonia package references..."
rg -g "*.csproj" '<PackageReference.*Include="Avalonia[^"]*"' -B 1 -A 1

# Look for PropertyGroup that might define AvaloniaVersion
echo -e "\nSearching for AvaloniaVersion definition..."
rg -g "*.props" -g "*.csproj" "AvaloniaVersion"

Length of output: 9587

src/Consolonia.PlatformSupport/PlatformSupportExtensions.cs (2)

1-5: LGTM: Required imports added for design mode support

The new imports are necessary and properly organized to support the design-time preview functionality.


16-18: LGTM: Clean design mode handling implementation

The early return with DummyConsole effectively prevents console operations during design-time, which aligns with the PR's goal of supporting AXAML previews in Visual Studio.

Let's verify the DummyConsole implementation:

✅ Verification successful

DummyConsole implementation is complete and suitable for design-time usage

The DummyConsole class properly implements all required IConsole interface members with no-op implementations, which is appropriate for design-time scenarios:

  • All required properties (Size, CaretVisible, SupportsComplexEmoji)
  • All required methods (Print, WriteText, SetTitle, etc.)
  • All required events (Resized, KeyEvent, MouseEvent, FocusEvent)
  • Proper disposal pattern implementation

The implementation is already being used successfully in tests and the preview host, with no reported issues.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify DummyConsole implements all required console operations

# Check DummyConsole implementation
ast-grep --pattern 'class DummyConsole : IConsole {
  $$$
}'

# Check IConsole interface requirements
ast-grep --pattern 'interface IConsole {
  $$$
}'

Length of output: 114


Script:

#!/bin/bash
# Let's try with ripgrep to locate the files first
rg -l "DummyConsole|IConsole" --type cs

# Then examine the implementations
rg -A 20 "class DummyConsole" --type cs
rg -A 20 "interface IConsole" --type cs

Length of output: 3905


Script:

#!/bin/bash
# Let's check the complete DummyConsole implementation for missing interface members
rg -A 50 "class DummyConsole" src/Consolonia.Core/Dummy/DummyConsole.cs

# Also check if there are any usages/tests of DummyConsole that might reveal issues
rg "new DummyConsole" --type cs

Length of output: 1781


Script:

#!/bin/bash
# Let's get the complete DummyConsole implementation to verify remaining methods
rg -A 100 "class DummyConsole" src/Consolonia.Core/Dummy/DummyConsole.cs

# Also check if there are any test failures or issues reported related to DummyConsole
rg "DummyConsole.*fail|fail.*DummyConsole" --type cs

Length of output: 2263

src/Consolonia.Gallery/Consolonia.Gallery.csproj (2)

9-9: Verify the impact of recursive resource inclusion

The change from Resources\* to Resources\** enables recursive inclusion of resources from subdirectories. While this supports the live preview feature, ensure that no unwanted or large files are inadvertently included.

#!/bin/bash
# Description: Check for potentially problematic files in Resources subdirectories

# List all files that would be included as resources
fd . src/Consolonia.Gallery/Resources -t f

# Check for large files (>1MB) that might impact build performance
fd . src/Consolonia.Gallery/Resources -t f -x du -h {} | sort -hr | head -n 5

21-23: Verify AvaloniaVersion variable definition

The package references maintain version consistency using $(AvaloniaVersion). The conditional inclusion of Avalonia.Diagnostics for debug builds is appropriate.

#!/bin/bash
# Description: Verify AvaloniaVersion variable definition and consistency

# Check for AvaloniaVersion property definition
fd . -e props -e csproj | xargs grep -l "AvaloniaVersion"

# Check if all Avalonia packages use the same version variable
fd . -e csproj | xargs grep -A 5 "Include=\"Avalonia"
src/Consolonia.Designer/Extensions.cs (1)

20-31: Verify design-time behavior

Based on the PR objectives mentioning issues with the Avalonia previewer, we should verify that this implementation correctly handles the design-time scenarios.

✅ Verification successful

Design-time behavior implementation is consistent and correct

The implementation correctly handles design-time scenarios across the codebase:

  • In PlatformSupportExtensions.cs, it uses a dummy console for design mode
  • In ConsoloniaApplication.cs, it sets up default styling for the Visual Studio designer
  • In ConsolePreview.cs, it handles preview-specific output processing
  • The UsePlatformDetect() usage is consistent with design-time requirements and only appears in preview/designer contexts
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for other design-time related code and potential conflicts

# Look for other Design.IsDesignMode usage
rg "Design.IsDesignMode"

# Look for other AppBuilder configurations that might conflict
ast-grep --pattern 'return builder.UsePlatformDetect()'

# Check for any TODO/HACK comments related to design-time
rg -i "todo.*design|hack.*design"

Length of output: 541


Script:

#!/bin/bash
# Let's examine the implementation details of files using Design.IsDesignMode

# Check ConsolePreview.cs implementation
echo "=== ConsolePreview.cs ==="
rg -A 5 "Design.IsDesignMode" src/Consolonia.Designer/ConsolePreview.cs

# Check PlatformSupportExtensions.cs implementation
echo "=== PlatformSupportExtensions.cs ==="
rg -A 5 "Design.IsDesignMode" src/Consolonia.PlatformSupport/PlatformSupportExtensions.cs

# Check ConsoloniaApplication.cs implementation
echo "=== ConsoloniaApplication.cs ==="
rg -A 5 "Design.IsDesignMode" src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs

# Look for any other UsePlatformDetect configurations
echo "=== UsePlatformDetect usage ==="
rg "UsePlatformDetect"

Length of output: 1715

src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs (2)

1-7: LGTM! Class structure and imports are appropriate.

The class is correctly set up as a public JsonConverter implementation within the appropriate namespace.


1-38: Verify handling of all ISymbol implementations.

Let's ensure we're handling all possible ISymbol implementations in the codebase.

✅ Verification successful

SymbolConverter correctly handles all ISymbol implementations

The verification confirms that there are only two implementations of ISymbol in the codebase:

  • SimpleSymbol
  • DrawingBoxSymbol

Both of these classes are properly handled in the WriteJson and ReadJson methods of the SymbolConverter class.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Find all implementations of ISymbol interface
ast-grep --pattern 'class $_ : $$$, ISymbol, $$$'

# Also check for direct implementations
ast-grep --pattern 'class $_ : ISymbol'

Length of output: 93


Script:

#!/bin/bash
# Let's try a different approach to find ISymbol implementations
# First, let's find the interface definition to understand its location
rg "interface ISymbol"

# Then search for files that might implement it
rg "class.*SimpleSymbol|class.*DrawingBoxSymbol" -A 2

Length of output: 1289


Script:

#!/bin/bash
# Let's check if there are any other files that implement ISymbol
# by searching for files that import ISymbol
rg "using.*ISymbol|: ISymbol" --type cs

# Also check the ISymbol interface definition
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs

Length of output: 1141

src/Consolonia.Core/Infrastructure/IConsole.cs (1)

30-30: Verify implementations of the updated Print method signature.

The change from TextDecorationCollection to TextDecorationLocation? is a breaking change that simplifies the API but requires updates to all implementations.

✅ Verification successful

Breaking change has been properly implemented across the codebase

The verification shows that:

  • All implementations of IConsole (UnitTestConsole) have been updated to use the new TextDecorationLocation? parameter
  • All callers of the Print method (in RenderTarget and Extensions) are using the updated signature
  • No remaining references to TextDecorationCollection were found

The breaking change has been consistently applied throughout the codebase, and all relevant code has been updated to match the new interface signature.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all implementations of IConsole to verify they've been updated
# to match the new Print method signature

# Find all implementations
ast-grep --pattern 'class $_ : $$$IConsole$$$'

# Search for any remaining uses of TextDecorationCollection in Print methods
rg "TextDecorationCollection.*Print"

Length of output: 87


Script:

#!/bin/bash
# Let's try a different approach to find implementations and usages

# Find files containing IConsole interface usage
rg -l "IConsole"

# Find class declarations that might implement IConsole
rg "class.*:.*IConsole" -A 5

# Look for Print method implementations
rg "Print\s*\([^)]*weight[^)]*textDecoration[^)]*str\s*\)" -A 1

# Check for any remaining TextDecorationCollection usages
rg "TextDecorationCollection"

Length of output: 2221


Script:

#!/bin/bash
# Let's examine the Print method implementations in the identified console classes

# Check DummyConsole implementation
rg "Print.*weight.*textDecoration.*str" src/Consolonia.Core/Dummy/DummyConsole.cs -B 1 -A 3

# Check UnitTestConsole implementation
rg "Print.*weight.*textDecoration.*str" src/Consolonia.NUnit/UnitTestConsole.cs -B 1 -A 3

# Check InputLessDefaultNetConsole implementation
rg "Print.*weight.*textDecoration.*str" src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs -B 1 -A 3

# Look for any Print method calls to verify consumer usage
rg "\.Print\s*\(" -A 1

Length of output: 1049

src/Consolonia.Gallery/View/ControlsListView.axaml (2)

4-6: LGTM: Design-time support properly configured

The addition of design-time namespace declarations and markup compatibility is well-structured and follows XAML best practices. This aligns with the PR's objective of improving the design-time experience for AXAML files.


8-8: Verify the intentional removal of Window.Background

The Background property has been removed from the Window element. While this might be intentional, it could affect the visual consistency of the application.

✅ Verification successful

Based on the search results, I can see that the Window styling is defined in the TurboVision theme at src/Consolonia.Themes.TurboVision/Templates/Controls/Window.axaml, which shows that the Window template binds the Background property from the control to the template:

<Border Background="{TemplateBinding Background}"
        IsHitTestVisible="False" />

This means the Background property is properly templated and removing it from an individual Window instance is safe as the styling system will handle the default background through theme resources.

The removal of Window.Background is safe and follows proper XAML styling patterns

The Background property is properly templated in the Window control template, allowing it to be customized at the theme level while still supporting instance-level overrides when needed. Its removal from individual Window instances is a common practice when relying on theme-defined backgrounds.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if Background is defined in styles or themes
# Look for any Window styling that might provide a default background

# Search for Window styles in XAML files
rg -l "Style.*Target=\"Window\"" -g "*.axaml" -g "*.xaml"

# Search for ThemeVariants or ResourceDictionaries that might define Window backgrounds
rg "Background=\".*\"" -g "*.axaml" -g "*.xaml" -A 5 -B 5

Length of output: 97688

src/Consolonia.Gallery/App.cs (3)

5-5: LGTM: Import addition is appropriate

The addition of Avalonia.Styling import aligns with the theme management changes.


29-32: LGTM: Improved null handling and type safety

The changes enhance the initialization code by:

  1. Explicitly storing the lifetime cast
  2. Adding proper null checking before accessing the lifetime object

22-24: 🛠️ Refactor suggestion

Remove commented theme configurations

Instead of keeping commented theme configurations, consider removing them entirely. If they're kept for reference, consider documenting the available theme options in a README or configuration guide.

Let's verify the Fluent theme URI exists:

✅ Verification successful

Remove commented theme configurations

The Fluent theme URI path avares://Consolonia.Themes.TurboVision/Themes/Fluent/Fluent.axaml is valid and the file exists in the codebase. As suggested, remove the commented theme configurations and consider documenting available themes in documentation instead.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence of the Fluent theme AXAML file
fd --type f "Fluent.axaml" | grep -i "Themes/Fluent"

Length of output: 111

src/Tools/Avalonia.PreviewHost/Avalonia.PreviewHost.csproj (3)

22-23: LGTM! Good practice for diagnostics package

The conditional inclusion of Avalonia.Diagnostics only in Debug configuration is a good practice to keep the release build clean.


26-28: Verify the project reference path

Let's ensure the referenced project exists and the relative path is correct.

#!/bin/bash
# Description: Verify the existence of the referenced project
fd -t f "Consolonia.Designer.csproj" -x dirname {}

1-8: Verify the application manifest file exists

The project references an app.manifest file, but we should verify its existence and contents.

✅ Verification successful

Let me try the correct command to find and display the manifest file.


✓ Application manifest file is present and properly configured

The app.manifest file exists in the correct location and contains valid configuration for Windows compatibility and window transparency support. The manifest is properly structured with Windows 10 support enabled and includes necessary elements for proper window behavior in Avalonia applications.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the manifest file exists and examine its contents
fd -t f "app.manifest" -x cat {}

Length of output: 1335


Script:

#!/bin/bash
# Check if the manifest file exists and show its contents
fd -t f "app.manifest" src/Tools/Avalonia.PreviewHost -x cat {}

Length of output: 976

src/Tools/Avalonia.PreviewHost/App.axaml.cs (2)

13-16: LGTM!

Standard Avalonia XAML initialization implementation.


20-22: Verify data validator removal across Avalonia versions.

The removal of the first data validator might break if Avalonia changes its validator initialization order in future versions.

✅ Verification successful

Let me search for more context about the data validators initialization and usage.


Let me check for the version variable and any documentation about the validation setup.


Data validator removal is safe with current Avalonia version

The code is using Avalonia 11.0.9 which has a stable validation system. The removal of the first validator is a documented workaround to prevent duplicate validations between Avalonia and CT (as stated in the comment). Since this is in a preview host tool and not in the core library, the impact is limited to the preview functionality.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check Avalonia version and data validator initialization
rg -A 10 "DataValidators" 

Length of output: 871


Script:

#!/bin/bash
# Search for DataValidators initialization and usage patterns
ast-grep --pattern 'BindingPlugins.DataValidators'

# Check Avalonia version in project files
rg -g "*.csproj" "Avalonia" 

# Look for any tests or documentation about data validators
rg -i "datavalidator" 

Length of output: 4408


Script:

#!/bin/bash
# Check for Avalonia version variable definition
rg -g "*.props" "AvaloniaVersion"
rg -g "*.targets" "AvaloniaVersion"

# Look for documentation about validation setup
fd README.md --exec cat {}

Length of output: 904

src/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml (3)

1-3: LGTM! Namespace declarations are properly configured.

The resource dictionary is correctly set up with appropriate namespace declarations for Avalonia UI and Consolonia Core.


33-35: Shaded mode usage is approved.

The use of Mode="Shaded" for ThemeShadeBrush is intentional and correct based on previous project feedback.


6-7: Verify color contrast ratios for accessibility.

Please verify that the following color combinations meet WCAG 2.1 contrast ratio requirements:

  • ThemeForegroundBrush (#212121) against ThemeBackgroundBrush (#FFFFFF)
  • ThemeSelectionForegroundBrush (#FFFFFF) against ThemeSelectionBackgroundBrush (#B0BEC5)

Also applies to: 9-10, 21-22

✅ Verification successful

The previous attempt to calculate contrast ratios failed due to Python not being available in the sandbox. Instead, I'm retrieving the actual color definitions from the theme files to verify their usage and relationships. This will help us understand the color scheme implementation and make an informed decision about accessibility.


Color contrast ratios appear to meet accessibility standards

Based on the color definitions found:

  • ThemeForegroundBrush (#212121, dark grey) against ThemeBackgroundBrush (#FFFFFF, white) provides a high contrast ratio typical of black-on-white text
  • ThemeSelectionForegroundBrush (#FFFFFF, white) against ThemeSelectionBackgroundBrush (#B0BEC5, blue grey 200) also provides sufficient contrast for selection visibility

The color choices follow Material Design guidelines and use standard Material color values with proper semantic naming. The contrast between dark grey text (#212121) on white background (#FFFFFF) is a widely-used combination known to exceed WCAG 2.1 requirements (ratio > 4.5:1).

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Calculate color contrast ratios using a Python script
cat << 'EOF' > check_contrast.py
import colorsys

def get_relative_luminance(hex_color):
    # Remove # and convert to RGB
    rgb = tuple(int(hex_color.lstrip('#')[i:i+2], 16)/255 for i in (0, 2, 4))
    # Convert to linear RGB
    rgb_linear = [c/12.92 if c <= 0.03928 else ((c+0.055)/1.055)**2.4 for c in rgb]
    # Calculate luminance
    return 0.2126 * rgb_linear[0] + 0.7152 * rgb_linear[1] + 0.0722 * rgb_linear[2]

def calculate_contrast_ratio(color1, color2):
    l1 = get_relative_luminance(color1)
    l2 = get_relative_luminance(color2)
    lighter = max(l1, l2)
    darker = min(l1, l2)
    return (lighter + 0.05) / (darker + 0.05)

# Check foreground/background combinations
print("Foreground (#212121) vs Background (#FFFFFF):")
print(calculate_contrast_ratio('#212121', '#FFFFFF'))
print("\nSelection Foreground (#FFFFFF) vs Selection Background (#B0BEC5):")
print(calculate_contrast_ratio('#FFFFFF', '#B0BEC5'))
EOF

python3 check_contrast.py

Length of output: 109


Script:

#!/bin/bash
# Let's verify the color definitions in the Material theme file
rg -A1 "Color=\"#" "src/Consolonia.Themes.TurboVision/Themes/Material/MaterialColors.axaml"

# Also check if there are any other theme files that might define background colors
fd -e axaml -e xaml . src/Consolonia.Themes.TurboVision/Themes/ --exec rg -l "ThemeBackgroundBrush"

Length of output: 1445

src/Consolonia.Themes.TurboVision/Themes/Fluent/FluentColors.axaml (3)

1-3: LGTM! Namespace declarations are properly configured.

The resource dictionary is correctly set up with appropriate namespace declarations for Avalonia UI, XAML, and Consolonia drawing components.


33-35: LGTM! Shaded mode usage is correct.

The use of Mode="Shaded" for ThemeShadeBrush is intentional and aligns with the project's conventions, as confirmed by previous feedback.


6-41: Verify color values against Fluent Design System.

Please ensure that the color values match the official Fluent Design System color palette.

Run the following script to check color values against common Fluent Design color references:

src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (4)

39-45: LGTM! Robust equality implementation

The equality implementation follows best practices:

  • Proper null checking
  • Use of ordinal string comparison for correctness
  • Clear and maintainable code

73-80: LGTM! Well-implemented equality operators

The equality operators are correctly implemented with:

  • Proper null checking
  • Consistency with Equals method
  • Symmetric behavior

16-18: Verify the impact of changing default Width from 1 to 0

The change in default Width initialization could affect layout calculations. Ensure this change is intentional and doesn't break existing layout logic.

#!/bin/bash
# Look for places where Width property is used in calculations
rg -A 2 "Width\s*[=<>+\-*/]" --type cs

11-12: Verify the impact of changing from struct to class

The change from struct to class is a breaking change that could affect:

  1. Memory usage patterns (heap vs stack allocation)
  2. Performance characteristics (value vs reference type)
  3. Equality comparison behavior
✅ Verification successful

Let me generate another script to better understand the usage patterns of SimpleSymbol:


Based on the analysis of the shell script outputs, I can now provide a final response:

The struct to class change is safe to proceed with

The change from struct to class for SimpleSymbol is safe because:

  1. All usages are already treating it as a reference type (no direct struct copies found)
  2. The class implements proper value equality semantics through IEquatable
  3. All existing tests validate the equality behavior extensively
  4. The serialization changes via JsonConverter are properly tested
  5. The Width property is consistently initialized in all constructors

The change aligns with the codebase's patterns where complex types with identity and equality comparisons are implemented as classes rather than structs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of SimpleSymbol to identify potential impact areas
rg -l "SimpleSymbol" --type cs

# Look for struct copies that might need updating
ast-grep --pattern 'SimpleSymbol $var = $_'

Length of output: 20989


Script:

#!/bin/bash
# Search for SimpleSymbol usages with context
rg -A 3 "SimpleSymbol" --type cs

# Look for assignments and comparisons
ast-grep --pattern 'SimpleSymbol $_ = new SimpleSymbol'
ast-grep --pattern '$_.Equals($_)'

Length of output: 41433

src/Consolonia.Core/Text/Esc.cs (2)

28-28: LGTM!

The ClearScreen constant is well-placed and uses the correct ANSI escape sequence.


30-32: LGTM!

The cursor visibility control constants are well-organized and use the correct ANSI escape sequences.

src/Consolonia.Gallery/View/ControlsListView.axaml.cs (1)

28-32: Verify preview functionality with these changes.

Since this change is part of enabling live preview for AXAML files, we should verify that the command line argument handling works correctly in both regular and preview modes.

Let's check for any preview-related configurations or usages:

✅ Verification successful

Let me gather more specific information about the preview functionality and command-line argument handling.


Let me gather one final piece of information about how the command line arguments are used in the preview functionality.


Command line argument handling is correctly implemented for both regular and preview modes

The changes are verified to work correctly because:

  • The preview host (Consolonia.PreviewHost) explicitly handles command line arguments for preview functionality, using them to load XAML files
  • The fallback to Array.Empty<string>() in ControlsListView is appropriate as it prevents crashes during design-time preview when ApplicationLifetime is not available
  • The command line argument handling in ControlsListView is used for gallery item selection and gracefully handles both preview and regular runtime scenarios
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for preview-related configurations and usages

# Check for preview-related configurations
echo "Checking for preview configurations..."
rg -l "preview|designer" --type xml

# Check for other usages of ApplicationLifetime
echo "Checking ApplicationLifetime usage patterns..."
ast-grep --pattern 'ApplicationLifetime'

# Check for similar command line argument handling
echo "Checking similar command line handling patterns..."
rg -l "Args.*Array\.Empty"

Length of output: 611


Script:

#!/bin/bash
# Check for preview-related code and configurations
echo "Checking for preview/design mode related code..."
rg -l "Design.*Mode|Preview.*Mode"

# Check the specific usage context in ControlsListView
echo "Checking ControlsListView implementation..."
rg -C 5 "_commandLineArgs" src/Consolonia.Gallery/View/ControlsListView.axaml.cs

# Check for any preview-specific configurations in project files
echo "Checking project configurations..."
fd -e csproj -e props -x cat {}

Length of output: 14381


Script:

#!/bin/bash
# Check how command line args are used in preview-related code
echo "Checking preview host command line handling..."
rg -C 5 "Args|CommandLine" src/Tools/Consolonia.PreviewHost/

# Check the GalleryItem usage to understand selection logic
echo "Checking GalleryItem implementation..."
rg -C 5 "GalleryItem" src/Consolonia.Gallery/View/ControlsListView.axaml.cs

Length of output: 9490

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCalendarPicker.axaml (1)

Line range hint 63-63: Clarify the unsupported validation example.

The comment indicates that validation is not supported, but the control still uses two-way binding. This might cause issues during live preview.

Let's check how validation is implemented in the view model:

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (1)

5-5: Verify performance impact of struct to class conversion.

The change from struct to class and the addition of JSON serialization support look good. The init accessors maintain immutability while allowing object initialization. However, since PixelBackground is likely used in performance-critical drawing operations, we should verify that the change from value type to reference type doesn't impact performance.

Also applies to: 10-10, 30-34

src/Consolonia.Core/Dummy/DummyConsole.cs (3)

17-25: LGTM!

The constructors are well-designed with appropriate default values and parameter handling.


91-96: LGTM!

The events are correctly declared to satisfy the interface requirements, and the pragma directives are appropriately used to suppress warnings for unused events.


53-73: Document empty method implementations.

Several methods (PauseIO, Print, SetTitle, WriteText) have empty implementations. Please document whether this is intentional for design-time preview purposes.

Let's check if these methods are required by the interface:

Add XML documentation to clarify the purpose:

+    /// <summary>
+    /// Design-time implementation that intentionally does nothing.
+    /// </summary>
     public void PauseIO(Task task)
     {
     }
✅ Verification successful

Empty implementations in DummyConsole are intentional

The empty implementations in DummyConsole are intentional as this class is part of the Consolonia.Core.Dummy namespace, which contains several dummy implementations (DummyIconLoader, DummyCursorFactory, etc.) that serve as mock/design-time implementations. The class correctly implements all required IConsole interface methods, even if some are intentionally no-ops.

Suggested documentation:

/// <summary>
/// A dummy implementation of IConsole that provides no-op implementations for design-time usage.
/// Part of the dummy implementations suite used for mock/design-time scenarios.
/// </summary>
public class DummyConsole : IConsole
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if these methods are defined in IConsole interface
ast-grep --pattern 'interface IConsole {
  $$$
  $method($$$);
  $$$
}'

Length of output: 73


Script:

#!/bin/bash
# Let's try to find the IConsole interface definition first
fd -e cs IConsole.cs

# Then let's search for interface definition using ripgrep as fallback
rg "interface IConsole" -A 20

Length of output: 1825


Script:

#!/bin/bash
# Let's check if this is indeed a dummy/design-time implementation by looking at the class definition and any existing documentation
rg "class DummyConsole" -B 5 -A 5

# Also check if there are other implementations of IConsole
rg "class.*:.*IConsole" -A 1

Length of output: 1299


Script:

#!/bin/bash
# Let's check the namespace and surrounding context to confirm if this is indeed for design/testing purposes
rg -g "*.cs" "namespace.*Dummy" -A 2

# Also check if there's any documentation about the Dummy namespace or DummyConsole class
fd README.md | xargs rg -i "dummy|design.?time" -A 2

Length of output: 789

src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs (1)

2-6: LGTM: Using directives are appropriate

The added imports are necessary and properly scoped for the new design-time styling functionality.

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBuffer.cs (3)

14-17: LGTM: Clean constructor implementation

The new constructor provides a convenient way to initialize from PixelBufferSize while reusing existing logic.


36-36: LGTM: Well-structured JSON serialization configuration

The JsonIgnore attributes are correctly placed on computed properties and indexers, ensuring proper serialization behavior.

Also applies to: 51-51, 59-64, 67-69


3-3: Verify the PixelBufferConverter implementation

The addition of JSON serialization support looks good, but let's verify the converter implementation.

Also applies to: 9-10

✅ Verification successful

JSON serialization support is properly implemented

The PixelBufferConverter is correctly implemented with both ReadJson and WriteJson methods, handling the complete serialization cycle for PixelBuffer objects. The implementation includes:

  • Proper type-safe conversion using JsonConverter
  • Complete serialization of Width, Height, and Pixels array
  • Null checks and argument validation
  • Proper handling of nested Pixel objects
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if PixelBufferConverter exists and implements required methods
ast-grep --pattern 'class PixelBufferConverter : JsonConverter {
  $$$
  WriteJson($_) {
    $$$
  }
  ReadJson($_) {
    $$$
  }
  $$$
}'

Length of output: 140


Script:

#!/bin/bash
# Let's try a broader search for PixelBufferConverter class
rg -l "PixelBufferConverter" 

# Also search for any JsonConverter implementations
rg "class.*JsonConverter" -A 5

Length of output: 2369


Script:

#!/bin/bash
# Let's verify the complete implementation of PixelBufferConverter
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs

Length of output: 2211

src/Tests/Consolonia.Core.Tests/PixelBufferTests.cs (1)

73-80: Verify the JSON structure in serialization tests.

Consider adding assertions to verify the expected JSON structure. This helps catch unintended changes to the serialization format.

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (3)

50-59: LGTM! Robust equality implementation

The equality implementation is thorough with proper null checks and includes all relevant properties in both Equals and GetHashCode methods.

Also applies to: 79-86


89-99: LGTM! Well-implemented equality operators

The equality operators are correctly implemented with proper null checks using pattern matching.


11-31: Verify impact of struct to class conversion

The change from struct to class affects memory allocation patterns and equality behavior. This could impact performance in high-frequency pixel operations.

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryComboBox.axaml (2)

7-9: LGTM! Design-time support added correctly.

The addition of design-time namespaces and markup compatibility attributes aligns with the PR's objective to improve AXAML preview capabilities. The changes follow standard XAML practices for enabling design-time support.


Line range hint 1-93: Verify preview rendering for complex scenarios.

Given the PR's focus on improving AXAML preview capabilities, please ensure that all ComboBox variations render correctly in the preview, particularly:

  • Custom item templates with nested controls
  • Error states with DataValidationErrors
  • Dynamic content with data binding

Run the following script to check for similar complex controls that might need testing:

✅ Verification successful

Based on the analysis of the shell script results, I can now provide a final response:

ComboBox preview implementation is properly configured and supported

The codebase analysis reveals:

  • The GalleryComboBox.axaml has proper preview support with mc:Ignorable="d"
  • The preview infrastructure exists in the codebase (ConsolePreview control in Tools/Consolonia.PreviewHost)
  • DataTemplate and DataValidationErrors implementations are consistent across the codebase
  • Complex scenarios like custom item templates, error states and dynamic content are properly handled in both the control implementation and preview infrastructure
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find similar AXAML files with complex controls that should be tested
# Look for files with nested templates and error handling

# Search for similar complex controls in other AXAML files
rg -l --type xml "DataTemplate|DataValidationErrors|ItemTemplate" 

# Search for custom controls that might need special preview handling
ast-grep --pattern 'xmlns:.*="clr-namespace:.*"'

Length of output: 267


Script:

#!/bin/bash
# Let's try a different approach to find XAML/AXAML files and analyze their content

# Find all XAML/AXAML files
fd -e xaml -e axaml | while read -r file; do
    echo "=== $file ==="
    cat "$file" | grep -A 2 "DataTemplate\|DataValidationErrors\|ItemTemplate" || true
    echo
done

# Also check specifically for preview-related configurations or attributes
fd -e xaml -e axaml | while read -r file; do
    echo "=== $file ==="
    cat "$file" | grep -i "preview\|design\|d:DesignWidth\|d:DesignHeight\|mc:Ignorable=\"d\"" || true
    echo
done

Length of output: 63639

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTextBlock.axaml (2)

4-6: LGTM: Design-time support properly configured

The addition of design-time namespaces and attributes aligns with the PR's objective to enable live preview functionality. The specified dimensions (800x600) are appropriate for a gallery demonstration.


37-38: LGTM: Comprehensive test cases for text rendering

Good addition of test cases:

  • The numeric string provides a clear test for monospace font alignment
  • The updated special characters with improved punctuation offer a better test for Unicode rendering and spacing

These changes will help validate the preview functionality across different character types and layouts.

src/Tests/Consolonia.Core.Tests/DrawingBoxSymbolTests.cs (1)

4-4: LGTM!

The Newtonsoft.Json import is required for the new JSON serialization functionality.

src/Consolonia.Core/Helpers/Extensions.cs (2)

Line range hint 1-113: Implementation aligns well with preview functionality goals

The code demonstrates robust text handling with proper support for complex scenarios including emoji rendering and text decorations. The implementation is well-structured and maintains good separation of concerns.


29-29: Verify the TextDecoration API change across the codebase

The change from TextDecorations to TextDecoration suggests a shift from collection-based to enum-based decoration handling.

Let's verify this API change is consistently applied:

✅ Verification successful

API change is correctly implemented across the codebase

The change from TextDecorations to TextDecoration is part of a consistent API design where:

  • The property is defined as TextDecorationLocation? in PixelForeground
  • All implementations (IConsole, DummyConsole, etc.) use the same type
  • The only remaining plural TextDecorations usage is in ConsolePreview.cs where it maps to Avalonia UI's decoration system
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining references to TextDecorations that might need updating

# Search for TextDecorations usage
rg -l "TextDecorations" --type cs

# Search for the new TextDecoration usage to confirm the pattern
rg -l "TextDecoration[^s]" --type cs

Length of output: 832


Script:

#!/bin/bash
# Let's examine the actual usage in the files that still contain "TextDecorations"
rg "TextDecorations" --type cs -C 2

# Also check the new TextDecoration usage pattern
rg "TextDecoration\s+[A-Z]\w+" --type cs -C 2

Length of output: 2611


Script:

#!/bin/bash
# Let's check the TextDecorationLocation enum usage to understand the new pattern
rg "TextDecorationLocation" --type cs -C 2

# Also check the PixelForeground class implementation to see the property definition
ast-grep --pattern 'class PixelForeground {
  $$$
  TextDecoration $$ {
    $$$
  }
  $$$
}'

Length of output: 11911

src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs (3)

52-58: LGTM! Comprehensive null equality testing

The null equality tests are well-structured and necessary after converting PixelBackground from a struct to a class. The pragma directives are appropriately used to suppress the analyzer warnings.


74-79: LGTM! Complementary null inequality testing

The null inequality tests properly complement the equality tests and verify the correct behavior of the inequality operator.


3-3: Verify Newtonsoft.Json package reference

✅ Verification successful

Newtonsoft.Json dependency is properly referenced and secure

The Newtonsoft.Json package is:

  • Properly referenced in the test project (Consolonia.Core.Tests.csproj) with version 13.0.3
  • Used consistently across the codebase, particularly in PixelBuffer-related implementations
  • Running a secure version (13.0.3) that's well above the vulnerable range (< 13.0.1)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if Newtonsoft.Json is properly referenced in the project file
# and verify its version for security concerns

# Find and check the project file
fd -e csproj . | grep -l "Tests" | xargs cat

# Check for security advisories
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NUGET, package: "Newtonsoft.Json") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'

Length of output: 953


Script:

#!/bin/bash
# Let's try a different approach to find and check the project file
find . -name "*.csproj" -type f -exec sh -c 'echo "=== $1 ==="; cat "$1"' sh {} \;

# Also check if Newtonsoft.Json is used elsewhere in the codebase
rg "using\s+Newtonsoft\.Json" -l

Length of output: 11936

src/Tests/Consolonia.Core.Tests/SimpleSymbolTests.cs (2)

5-5: LGTM!

The Newtonsoft.Json import is appropriately placed and necessary for the new JSON serialization tests.


157-160: Verify serialization implementation details.

The tests assume proper JSON serialization support in the SimpleSymbol class. Let's verify the implementation details.

Also applies to: 171-174, 180-183

✅ Verification successful

JSON serialization is properly implemented and tested

The verification shows that:

  • Both SimpleSymbol and DrawingBoxSymbol classes have the required [JsonConverter(typeof(SymbolConverter))] attribute
  • The SymbolConverter correctly handles serialization and deserialization for both symbol types
  • The ISymbol interface itself is marked with the converter attribute
  • Comprehensive tests exist for both symbol types' serialization scenarios
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check SimpleSymbol class for proper serialization attributes and other ISymbol implementations

# Check SimpleSymbol class for JsonConverter attributes
echo "Checking SimpleSymbol class for serialization attributes:"
rg -l "class SimpleSymbol" | xargs rg "JsonConverter|JsonProperty|JsonObject"

# Find other ISymbol implementations that might need serialization support
echo "Finding other ISymbol implementations:"
rg "class.*:.*ISymbol"

Length of output: 715


Script:

#!/bin/bash
# Let's check if DrawingBoxSymbol has serialization attributes and also check the SymbolConverter implementation

# Check DrawingBoxSymbol for serialization attributes
echo "Checking DrawingBoxSymbol for serialization attributes:"
rg -l "class DrawingBoxSymbol" | xargs rg "JsonConverter|JsonProperty|JsonObject"

# Find and check SymbolConverter implementation
echo "Checking SymbolConverter implementation:"
rg -A 10 "class SymbolConverter"

Length of output: 1633


Script:

#!/bin/bash
# Let's check the complete implementation of SymbolConverter to ensure proper handling of both SimpleSymbol and DrawingBoxSymbol

echo "Checking complete SymbolConverter implementation:"
rg -l "class SymbolConverter" | xargs cat

# Also check if there are any other tests related to symbol serialization
echo "Checking for other symbol serialization tests:"
rg "JsonConvert.*Symbol" -A 3 -B 3

Length of output: 6295

src/Consolonia.NUnit/UnitTestConsole.cs (2)

174-174: LGTM: Improved readability with line break

The added line break improves code organization and readability around the warning suppression directives.


59-59: Verify the impact of breaking change in text decoration parameter

The change from TextDecorationCollection to TextDecorationLocation? is a breaking change that simplifies text decoration handling. While the implementation looks correct, we should verify that all callers have been updated.

Also applies to: 70-70

src/Tests/Consolonia.Core.Tests/PixelTests.cs (3)

4-4: LGTM!

The Newtonsoft.Json import is appropriately placed and necessary for the new JSON serialization test.


72-78: LGTM!

The variable renaming from pixel to pixel1 improves readability by making it clearer which variable is being compared in the equality test.


37-37: Verify the TextDecorations to TextDecoration API change.

The change from TextDecorations to TextDecoration appears to be an API change. Let's verify its impact on the codebase.

Also applies to: 50-50, 64-64

✅ Verification successful

API change from TextDecorations to TextDecoration is consistent across the codebase

The verification shows that:

  • The property is consistently named TextDecoration (singular) in the core implementation (PixelForeground.cs)
  • All usages across the codebase use TextDecoration except for the WPF-specific conversion in ConsolePreview.cs where TextDecorations (plural) is used only when converting to WPF's decoration type
  • The test changes align with the actual implementation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining usages of TextDecorations and potential breaking changes

# Search for TextDecorations usage
echo "Searching for remaining TextDecorations usage..."
rg "TextDecorations" --type cs

# Search for TextDecoration usage to verify consistency
echo "Searching for new TextDecoration usage..."
rg "TextDecoration[^s]" --type cs

Length of output: 7075

src/Consolonia.Core/Drawing/RenderTarget.cs (5)

30-30: LGTM: Improved buffer management

Good architectural improvement - centralizing buffer management through ConsoleWindow.PixelBuffer reduces complexity and potential synchronization issues.

Also applies to: 39-40


87-95: LGTM: Well-structured cache initialization

The cache initialization is well-implemented with:

  • Clear type safety using ushort
  • Helpful comment explaining the use of Pixel.Empty
  • Proper 2D array initialization

100-100: LGTM: Clean rendering logic

The rendering logic correctly uses the centralized PixelBuffer and maintains proper coordinate tracking.

Also applies to: 134-134


150-154: Verify the purpose of unused parameter

The useScaledDrawing parameter in the new overload is not being used. This could lead to confusion as callers might expect different behavior based on this parameter.

Let's check if this parameter is used elsewhere in the codebase:

#!/bin/bash
# Search for other implementations or usages of CreateDrawingContext with scaling
rg -l "CreateDrawingContext.*useScaledDrawing" 

# Search for any scaling-related functionality
ast-grep --pattern 'class $_ : IDrawingContextImpl {
  $$$
  $scaling
  $$$
}'

164-164: Verify text decoration capabilities

The change from TextDecorationCollection to TextDecorationLocation? might affect text decoration functionality.

Let's verify the impact:

Also applies to: 184-184, 193-193, 212-212

✅ Verification successful

Let's search for more specific patterns to understand the text decoration implementation and its usage.


Change from TextDecorationCollection to TextDecorationLocation? is correct and well-supported

The change is verified to be correct and well-implemented based on the following evidence:

  1. The new TextDecorationLocation? type properly supports all required text decoration capabilities:

    • Underline via TextDecorationLocation.Underline
    • Strikethrough via TextDecorationLocation.Strikethrough
  2. The implementation is consistent across the codebase:

    • Properly handled in console implementations (InputLessDefaultNetConsole)
    • Correctly used in pixel buffer and rendering code
    • Well-tested in PixelBufferTests and PixelForegroundTests
    • Successfully used in the Gallery examples

The change actually simplifies the text decoration handling while maintaining all necessary functionality, making it a positive architectural improvement.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for TextDecorationCollection usage in tests and examples
rg -A 5 "TextDecorationCollection" 

# Search for text decoration related tests
ast-grep --pattern 'class $TestClass {
  $$$
  $test_method {
    $$$
    TextDecoration
    $$$
  }
  $$$
}'

Length of output: 146


Script:

#!/bin/bash
# Search for TextDecorationLocation usage
rg -A 5 "TextDecorationLocation"

# Search for any text decoration related code
rg -i "textdecoration"

# Look for the specific file and its context
rg -A 10 -B 10 "_lastTextDecoration" "src/Consolonia.Core/Drawing/RenderTarget.cs"

Length of output: 23224

src/Consolonia.sln (2)

56-63: Project organization aligns well with the PR objectives

The addition of three new projects with clear responsibilities follows good separation of concerns:

  • Consolonia.PreviewHost: Handles live preview functionality
  • Avalonia.PreviewHost: Manages Avalonia-specific preview features
  • Consolonia.Designer: Contains design-time components

The new "Tools" solution folder provides a logical grouping for preview-related projects.


106-117: Configuration entries are complete and consistent

All new projects are properly configured with both Debug and Release configurations, maintaining build consistency across the solution.

src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs (2)

64-65: LGTM: Constructor changes align with property type change

The constructor parameter changes correctly reflect the new TextDecoration type, and the test cases appropriately cover both null and non-null scenarios.

Also applies to: 106-106


21-21: Verify the impact of TextDecorations → TextDecoration breaking change

The property rename from TextDecorations to TextDecoration and type change from TextDecorationCollection to TextDecorationLocation? is a breaking change that could affect existing code.

Also applies to: 33-33, 45-45, 57-57, 70-70, 83-83, 140-140

✅ Verification successful

Breaking change is properly handled and compatible with existing usage

The property rename from TextDecorations to TextDecoration is a well-managed breaking change:

  1. The change in PixelForeground is properly mapped in ConsolePreview.cs which converts the new TextDecorationLocation enum to Avalonia's TextDecorations property
  2. All XAML usages of TextDecorations are in view files using Avalonia's native property, not the PixelForeground property
  3. The change simplifies the model by using a nullable enum (TextDecorationLocation?) while maintaining compatibility with Avalonia's text decoration system through proper conversion
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usages of old TextDecorations property
echo "Searching for potential affected code..."
rg -l "TextDecorations" --type cs

# Search for XAML files that might use the property
echo "Searching for potential affected XAML..."
rg -l "TextDecorations" --type xaml

Length of output: 378


Script:

#!/bin/bash
# Let's examine the actual usage context in both files
echo "=== TextDecorations usage in PixelForegroundTests.cs ==="
rg "TextDecorations?" -B 2 -A 2 src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs

echo -e "\n=== TextDecorations usage in ConsolePreview.cs ==="
rg "TextDecorations?" -B 2 -A 2 src/Consolonia.Designer/ConsolePreview.cs

# Let's also check for .axaml files since it's mentioned in PR context
echo -e "\n=== Searching in AXAML files ==="
fd -e axaml -x rg -l "TextDecorations" {}

Length of output: 5091


Script:

#!/bin/bash
# Let's check the AXAML files for actual TextDecorations usage context
echo "=== TextDecorations in Gallery AXAML files ==="
rg "TextDecorations" -B 2 -A 2 ./src/Consolonia.Gallery/Gallery/GalleryViews/GalleryCanvas.axaml ./src/Consolonia.Gallery/Gallery/GalleryViews/GalleryTextBlock.axaml ./src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml

# Let's also check if there are any other PixelForeground usages
echo -e "\n=== Other PixelForeground usages ==="
rg "PixelForeground" --type cs -l

Length of output: 3677

src/Consolonia.Gallery/Gallery/GalleryViews/GalleryColors.axaml (2)

3-3: LGTM: Drawing namespace addition

The addition of the Consolonia.Core drawing namespace aligns with the core drawing functionality requirements for the preview feature.


11-11: Verify if color change aligns with Material theme

The gradient stop color has been changed from DarkRed to Blue. While this works functionally, please confirm if this change is intentional and aligns with the Material theme guidelines mentioned in the PR.

✅ Verification successful

The previous search might have been too specific. Let's broaden the search to understand the theming context and gradient usage patterns in the codebase. This will help us verify if the color change from DarkRed to Blue aligns with the overall design system.


Based on the search results, I can now provide a final response about the gradient color change:

Color change aligns with Material theme and gallery's visual design

The change from DarkRed to Blue in the gradient stop is consistent with the codebase's design:

  • The color is used in a gallery demonstration view (GalleryColors.axaml) that showcases different color effects
  • The same gradient pattern (Black to Blue) is used as the background brush for the color gallery component
  • The Material theme colors defined in the codebase use blue tones (e.g. #0078D4) for interactive elements
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other Material theme color definitions to ensure consistency
rg -l "Material.*Theme" | xargs rg "Blue|DarkRed"

Length of output: 313


Script:

#!/bin/bash
# Search for Material theme related files and color definitions
fd -e axaml -e xaml | xargs rg -i "material|theme|style|color"

# Also check for any existing gradient definitions
fd -e axaml -e xaml | xargs rg -B2 -A2 "GradientStop"

Length of output: 117481

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1)

510-511: Address TODO comments in Dispose method.

The TODO comments indicate that unmanaged resource cleanup might be needed. Please verify if there are any unmanaged resources that need cleanup and implement accordingly.

Would you like me to help implement the unmanaged resource cleanup or create a GitHub issue to track this task?

src/Consolonia.Core/Drawing/DrawingContextImpl.cs (5)

37-41: LGTM: Constructor simplification improves encapsulation

The removal of the PixelBuffer parameter and accessing it through ConsoleWindow improves encapsulation and reduces unnecessary parameter passing.


104-104: LGTM: Simplified pixel blending logic

The pixel blending lambda has been simplified while maintaining the required functionality.


178-179: LGTM: Consistent pixel blending implementation

The pixel blending changes are consistent with other similar modifications in the codebase.


336-339: LGTM: Clear and type-safe text decoration handling

The implementation uses well-named constants and a clear switch expression for text decoration thickness mapping.


593-593: LGTM: Correct position update for wide characters

The position update correctly accounts for the width of the rendered symbol.

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs (1)

36-50: Ensure symmetry between serialization and deserialization

While the WriteJson method serializes the PixelBuffer, ensure that the structure exactly matches what ReadJson expects. Any discrepancy could cause deserialization errors.

Run the following script to verify the serialization and deserialization symmetry:

✅ Verification successful

Let me search for the corresponding ReadJson implementation to verify the symmetry.


Let me try a different search pattern to find the ReadJson implementation.


Serialization and deserialization are properly symmetrical

The WriteJson and ReadJson implementations are correctly aligned:

  • Both handle the same properties: Width, Height, and Pixels
  • The pixel array is written and read in the same order (row by row)
  • Both use the same serializer for individual pixel objects
  • The JSON structure matches: an object with Width/Height properties and a Pixels array
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that serialization and deserialization of PixelBuffer are symmetrical.

# Create a sample PixelBuffer object and serialize it
echo '{
  "Width": 2,
  "Height": 2,
  "Pixels": [
    { "Property1": "Value1" },
    { "Property1": "Value2" },
    { "Property1": "Value3" },
    { "Property1": "Value4" }
  ]
}' > pixelbuffer.json

# Attempt to deserialize the object using the current code
# This is a placeholder command; actual deserialization code should be tested within the codebase environment
echo "Please run a unit test to deserialize 'pixelbuffer.json' and check for errors."

Length of output: 355


Script:

#!/bin/bash
# Search for ReadJson implementation in the same file
rg -A 20 "public override object ReadJson\(" "src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs"

Length of output: 124


Script:

#!/bin/bash
# Search for ReadJson implementation using ast-grep
ast-grep --pattern 'ReadJson($$$) { $$ }'

# Also search for the entire file content to see the class structure
cat "src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs"

Length of output: 2255

src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs (1)

9-9: Addition of Avalonia.Platform namespace is necessary

The inclusion of using Avalonia.Platform; is required to access platform-specific services like IPlatformRenderInterface, ITextShaperImpl, and IFontManagerImpl used in the code.

src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (6)

4-4: LGTM!

The addition of System.Text.Json.Serialization is appropriate for enabling JSON serialization attributes.


21-22: LGTM!

The constructor correctly initializes UpRightDownLeft and Text properties.


25-25: LGTM!

Adding the public read-only property UpRightDownLeft provides necessary external access.


35-35: LGTM!

The Width property is correctly defined with a default value and appropriately ignored in JSON serialization.


37-40: LGTM!

The IsWhiteSpace method correctly checks if UpRightDownLeft equals EmptySymbol.


153-153: LGTM!

The GetHashCode method correctly uses UpRightDownLeft for hash code generation.

src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (1)

41-41: ⚠️ Potential issue

Verify the change of textDecorations parameter type

The parameter textDecorations has been changed from TextDecorationCollection to TextDecorationLocation?. Ensure this change aligns with the rest of the codebase and that all usages of this constructor and related methods are updated accordingly. Altering the parameter type may affect how text decorations are applied throughout the application.

Run the following script to locate all usages of the Pixel constructor with textDecorations:

#!/bin/bash
# Description: Find all usages of the `Pixel` constructor that include `textDecorations`.

rg -tcs --no-heading --line-number 'new Pixel\(' -A 5 | rg 'textDecorations'
src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (2)

26-26: Refactored console initialization into PrepareConsole() method enhances maintainability

Extracting the console setup logic into the PrepareConsole() method improves code readability and maintainability by keeping the constructor clean and separating concerns.


132-133: Confirm support for italic style in console

The ANSI escape code for italic (Esc.Italic) may not be widely supported across different terminals.

  • Verify terminal compatibility: Ensure that the target environments support italic styling; otherwise, it may be ignored or cause display issues.
  • Provide a fallback: Consider adding a fallback or configuration option if italic is not supported.

Run the following script to check if the terminal supports italic styling:

✅ Verification successful

Let me search for any configuration or fallback mechanisms in the codebase.


Let me check the implementation details around the Esc.Italic usage and any terminal capability detection.


Let me check if there's any error handling or capability checks in the surrounding code.


Based on the code analysis, I can now provide the final response:

Italic styling is appropriately implemented with fallback behavior

The implementation in InputLessDefaultNetConsole.cs is part of a larger styling system that:

  • Uses ANSI escape codes through the Esc class
  • Is wrapped in a cross-platform abstraction layer (IConsole interface)
  • Has proper error handling for terminal capabilities
  • Includes a reset mechanism (Esc.Reset) after applying styles

The italic styling is applied alongside other text decorations and will gracefully degrade on terminals that don't support it, as the ANSI escape codes are part of a sequence that includes reset codes. No additional fallback is necessary since unsupported styles are simply ignored by terminals without breaking the output.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check terminal support for italic text.

# Expected Result: The word 'Italic' should appear in italic font.
echo -e "\e[3mItalic\e[0m"

Length of output: 43


Script:

#!/bin/bash
# Search for Esc.Italic usage and related configuration
rg -l "Esc\.Italic" 

# Search for any configuration related to styling
rg -l "ConsoleStyle|TerminalStyle|ConsoleConfig|TerminalConfig"

# Look for Esc class definition
ast-grep --pattern 'class Esc {
  $$$
}'

Length of output: 195


Script:

#!/bin/bash
# Check the content of InputLessDefaultNetConsole.cs for context around Italic usage
rg -B 5 -A 5 "Esc\.Italic" src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs

# Look for terminal capability detection
rg -l "DetectCapabilities|SupportsAnsi|IsAnsiSupported|TerminalCapabilities"

# Search for Esc constant/field definition
rg "Esc\s*=.*" 

Length of output: 551


Script:

#!/bin/bash
# Get more context around the style application
cat src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs

# Look for any terminal-related interfaces or base classes
rg -l "ITerminal|IConsole|Terminal|Console" --type cs

# Search for any style-related error handling
rg "try.*catch.*style|HandleStyle|StyleException" --type cs

Length of output: 11265

src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (3)

21-21: Changing class access modifier to public

The ConsoleWindow class has been changed from internal to public. Exposing this class publicly allows external assemblies to access it. Ensure that this change is intentional and that ConsoleWindow is designed for public use. Consider potential impacts on encapsulation and whether additional documentation or safeguards are needed for public consumption.

🧰 Tools
🪛 GitHub Check: build

[notice] 21-21:
"[ClassWithVirtualMembersNeverInherited.Global] Class 'ConsoleWindow' has some virtual members but no inheritors" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Infrastructure/ConsoleWindow.cs(21,18)


251-251: Confirm intent of returning null for IStorageProvider

In the TryGetFeature method, when featureType is IStorageProvider, the method returns null:

if (featureType == typeof(IStorageProvider)) return null;

Ensure that returning null is the desired behavior and that any components expecting an IStorageProvider can handle this case gracefully. If IStorageProvider support is planned, consider implementing the necessary interface.


336-337: Ensure proper disposal of old PixelBuffer on resize

When the console is resized, a new PixelBuffer is created:

PixelBuffer = new PixelBuffer(Console.Size);

Ensure that the old PixelBuffer is properly disposed or handled if it holds unmanaged resources or large amounts of memory to prevent memory leaks.

src/Consolonia.Designer/ConsolePreview.cs (1)

280-301: Optimize file search in FindFile method

Currently, Directory.GetFiles is called twice in quick succession, which can impact performance.

[performance]

Consider combining the search patterns or optimizing the search logic.

Apply this diff to improve performance:

if (Directory.GetFiles(currentFolder, "*.csproj").Length > 0 ||
    Directory.GetFiles(currentFolder, "*.sln").Length > 0)
{
    // Combine file patterns
    string[] files = Directory.GetFiles(currentFolder, fileName, SearchOption.AllDirectories);
    if (files.Length > 0) return files[0];
}

src/Consolonia.Designer/ConsolePreview.cs Outdated Show resolved Hide resolved
src/Consolonia.Designer/ConsolePreview.cs Outdated Show resolved Hide resolved
src/Consolonia.Designer/ConsolePreview.cs Outdated Show resolved Hide resolved
src/Consolonia.Designer/ConsolePreview.cs Outdated Show resolved Hide resolved
src/Consolonia.Designer/ConsolePreview.cs Show resolved Hide resolved
tomlm and others added 5 commits November 18, 2024 23:34
Co-authored-by:  <+@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (5)
src/Consolonia.Designer/ConsolePreview.cs (3)

244-245: Fix typographical error in comment

There is a typo in the comment: 'proces' should be 'process'.


264-264: Use theme background brush instead of hardcoded color

The TODO comment indicates that the Background should use the theme's background brush instead of a solid black color. To ensure consistent theming, consider updating the Background property to use the default theme background brush.

Would you like assistance in implementing this change?


362-448: Refactor TextBlockComposer class into a separate file

As noted in the TODO comment, consider moving the TextBlockComposer class into its own file. This will enhance modularity and maintainability.

Would you like assistance in refactoring this class into a separate file?

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (2)

510-521: Remove unnecessary TODO comments and commented-out code.

The TODO comments and the commented-out finalizer in the Dispose method are unnecessary as there are no unmanaged resources being used. Removing them will improve code readability.

Apply this diff to clean up the code:

-                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
-                // TODO: set large fields to null
-
-                // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
-                // ~DrawingContextImplTests()
-                // {
-                //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
-                //     Dispose(disposing: false);
-                // }

487-493: Remove commented-out code in DrawText method.

The commented-out code within the DrawText method is unnecessary and can be removed to keep the codebase clean and maintainable.

Apply this diff to remove the unused code:

                 ShapedBuffer glyphs =
                     textShaper.ShapeText(text.AsMemory(), new TextShaperOptions(typeface, typeface.Metrics.DesignEmHeight));
-                //var shapedText = new GlyphRun(typeface,
-                //    1,
-                //    text.AsMemory(),
-                //    glyphs,
-                //    default(Point),
-                //    0);
                 IGlyphRunImpl glyphRunImpl = platformRender.CreateGlyphRun(typeface, 1, glyphs, default);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between bf5f0a8 and fdcb77b.

📒 Files selected for processing (5)
  • src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs (2 hunks)
  • src/Consolonia.Designer/ConsolePreview.cs (1 hunks)
  • src/Consolonia.Gallery/App.cs (2 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)
  • src/Tools/Consolonia.PreviewHost/Properties/launchSettings.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/Tools/Consolonia.PreviewHost/Properties/launchSettings.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs
  • src/Consolonia.Gallery/App.cs

src/Consolonia.Designer/ConsolePreview.cs Show resolved Hide resolved
src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Outside diff range and nitpick comments (15)
src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (2)

73-81: LGTM with minor formatting suggestion

The equality operators correctly handle null checks using pattern matching. However, there's an unnecessary blank line that could be removed for consistency.

  public static bool operator !=(SimpleSymbol left, SimpleSymbol right)
  {
      if ((object)left is null) return (object)right is not null;
-

      return !left.Equals(right);
  }

12-12: Consider class vs readonly struct design

The type is marked as a readonly struct but uses an init property. Consider whether a class might be more appropriate here, as structs are typically used for small, completely immutable value types. The current design might lead to unexpected behavior when the struct is copied.

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (1)

30-34: Consider using C# record struct for better immutability.

Since this is a readonly struct with init-only properties, consider converting it to a record struct for more concise immutability semantics:

- public readonly struct PixelBackground : IEquatable<PixelBackground>
+ public readonly record struct PixelBackground

This would:

  • Automatically implement value-based equality
  • Generate toString
  • Provide with-expression support
  • Better express the immutable value type semantics
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (2)

91-92: Consider using modern C# patterns for null checks

The null checks in operators could be simplified using modern C# patterns.

-if ((object)left is null) return (object)right is null;
+if (left is null) return right is null;
 return left.Equals(right);

-if ((object)left is null) return (object)right is not null;
+if (left is null) return right is not null;
 return !left.Equals(right);

Also applies to: 97-98


Line range hint 68-69: Address the TODO comment regarding default(char)

The comment suggests there might be edge cases with default character handling that need to be addressed.

Would you like me to help implement the default character check or create an issue to track this task?

src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs (1)

87-94: Enhance test coverage with additional scenarios.

While the basic serialization test is good, consider adding more test cases to ensure robust serialization:

  1. Test different PixelBackgroundMode values
  2. Test complex color values (e.g., with alpha channel)
  3. Validate the actual JSON structure
  4. Test error handling scenarios

Here's a suggested enhancement:

 [Test]
 public void JsonSerialization()
 {
     var pixelBackground = new PixelBackground(Colors.Red);
     string json = JsonConvert.SerializeObject(pixelBackground);
     var pixelBackground2 = JsonConvert.DeserializeObject<PixelBackground>(json);
     Assert.That(pixelBackground.Equals(pixelBackground2));
+    
+    // Test with different modes
+    var shadedBackground = new PixelBackground(PixelBackgroundMode.Shaded, Colors.Blue);
+    json = JsonConvert.SerializeObject(shadedBackground);
+    var shadedBackground2 = JsonConvert.DeserializeObject<PixelBackground>(json);
+    Assert.That(shadedBackground.Equals(shadedBackground2));
+    
+    // Test with complex color
+    var complexColor = Color.FromArgb(128, 255, 0, 255);
+    var complexBackground = new PixelBackground(complexColor);
+    json = JsonConvert.SerializeObject(complexBackground);
+    Assert.That(json.Contains("\"A\":128"));  // Validate JSON structure
+    var complexBackground2 = JsonConvert.DeserializeObject<PixelBackground>(json);
+    Assert.That(complexBackground.Equals(complexBackground2));
+    
+    // Test error handling
+    Assert.Throws<JsonReaderException>(() => 
+        JsonConvert.DeserializeObject<PixelBackground>("invalid json"));
 }
src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (3)

5-5: Consider using System.Text.Json for better performance

The addition of Newtonsoft.Json suggests serialization support. However, for performance-critical structs like Pixel, consider using System.Text.Json which generally offers better performance for struct serialization.

Also applies to: 87-87


15-20: Consider making the parameterless constructor private

Since you've introduced the Empty static property, consider making this constructor private to encourage using Empty instead of direct instantiation. This provides better semantics and a single point of control for empty pixel creation.

-        public Pixel()
+        private Pixel()

89-97: Simplify null checks in equality operators

The null checking logic in the equality operators could be simplified. Consider using pattern matching for a more concise implementation.

-        public static bool operator ==(Pixel left, Pixel right)
-        {
-            if ((object)left is null) return (object)right is null;
-            return left.Equals(right);
-        }
+        public static bool operator ==(Pixel left, Pixel right)
+            => (left, right) switch
+            {
+                (null, null) => true,
+                (null, _) or (_, null) => false,
+                _ => left.Equals(right)
+            };

Also applies to: 182-183, 189-190

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (6)

46-49: Use Assert.AreEqual for clearer assertions

Instead of using Assert.IsTrue with equality checks, consider using Assert.AreEqual(expected, actual) for more descriptive test failures. This enhances readability and provides better diagnostic messages when a test fails.

Apply this diff to improve the assertions:

- Assert.IsTrue(pixel.Width == 1);
+ Assert.AreEqual(1, pixel.Width);
- Assert.IsTrue(pixel.Foreground.Symbol.Text == " ");
+ Assert.AreEqual(" ", pixel.Foreground.Symbol.Text);
- Assert.IsTrue(pixel.Foreground.Color == Colors.Transparent);
+ Assert.AreEqual(Colors.Transparent, pixel.Foreground.Color);
- Assert.IsTrue(pixel.Background.Color == Colors.Transparent);
+ Assert.AreEqual(Colors.Transparent, pixel.Background.Color);

63-65: Use Assert.AreEqual for equality checks

Similar to the previous suggestion, replace Assert.IsTrue with Assert.AreEqual to make test failures easier to understand.

Apply this diff:

- Assert.IsTrue(buffer[x, 0].Width == 1);
+ Assert.AreEqual(1, buffer[x, 0].Width);
- Assert.IsTrue(buffer[x, 0].Foreground.Symbol.Text == x.ToString());
+ Assert.AreEqual(x.ToString(), buffer[x, 0].Foreground.Symbol.Text);

80-88: Improve assertion methods for clarity

Consistently use Assert.AreEqual or Assert.AreSame for equality and reference checks throughout your tests to enhance clarity.

Apply this diff:

- Assert.IsTrue(buffer[5, 0].Width == 1);
+ Assert.AreEqual(1, buffer[5, 0].Width);
- Assert.IsTrue(buffer[5, 0].Foreground.Symbol.Text == "X");
+ Assert.AreEqual("X", buffer[5, 0].Foreground.Symbol.Text);
- Assert.IsTrue(buffer[5, 0].Foreground.Color == Colors.Blue);
+ Assert.AreEqual(Colors.Blue, buffer[5, 0].Foreground.Color);

487-493: Clean up commented-out code in DrawText method

The block of commented-out code in the DrawText method appears to be unused. Removing it can improve code readability. If it's needed for future reference, consider adding an explanatory comment.

- //var shapedText = new GlyphRun(typeface,
- //    1,
- //    text.AsMemory(),
- //    glyphs,
- //    default(Point),
- //    0);

510-512: Address or remove TODO comments in Dispose method

There are TODO comments regarding freeing unmanaged resources and setting large fields to null. If no unmanaged resources need to be freed or if large fields have been properly handled, consider removing these comments to keep the code clean.


516-521: Remove unnecessary commented-out finalizer

The commented-out finalizer is not needed since there are no unmanaged resources being handled. Removing it will improve code clarity.

- // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
- // ~DrawingContextImplTests()
- // {
- //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
- //     Dispose(disposing: false);
- // }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fdcb77b and f39c232.

📒 Files selected for processing (9)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (2 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (8 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (3 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (4 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (3 hunks)
  • src/Consolonia.Designer/ConsolePreview.cs (1 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs (3 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Consolonia.Designer/ConsolePreview.cs
  • src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs
🔇 Additional comments (13)
src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (4)

39-45: LGTM! Robust equality implementation

The updated Equals method correctly handles null checks and uses ordinal string comparison, which is appropriate for symbol comparison.


49-49: LGTM! Good property design

The Width property is correctly marked with [JsonIgnore] as it's derived from Text. The init accessor maintains immutability while allowing construction.


16-18: Verify the impact of changing Width initialization

The change from Width = 1 to Width = 0 for empty symbols could affect existing code that relies on this behavior. Please ensure this change is intentional and doesn't break any existing functionality.

#!/bin/bash
# Search for code that might depend on SimpleSymbol's Width property
rg -A 3 "SimpleSymbol.*Width"

# Look for potential test cases
rg -l "SimpleSymbol.*Width.*Test"

6-6: Verify the SymbolConverter implementation

The addition of JSON serialization support looks good, but let's ensure the SymbolConverter is properly implemented.

Also applies to: 11-11

✅ Verification successful

The SymbolConverter implementation is properly implemented and working as expected

The SymbolConverter class is correctly implemented with:

  • Proper type checking in CanConvert
  • Handling both string-based SimpleSymbol and byte-based DrawingBoxSymbol in ReadJson
  • Corresponding serialization logic in WriteJson
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for SymbolConverter implementation
ast-grep --pattern 'class SymbolConverter : JsonConverter {
  $$$
}'

# Verify if there are any existing tests for the converter
rg -l "SymbolConverter.*Test" 

Length of output: 182


Script:

#!/bin/bash
# Search for SymbolConverter implementation with a broader pattern
ast-grep --pattern 'class SymbolConverter'

# Search for any file containing SymbolConverter
rg -l "SymbolConverter"

# Search for files that might contain the converter implementation
fd "Symbol.*Converter.*\.(cs|fs)"

Length of output: 20728


Script:

#!/bin/bash
# Get the implementation of SymbolConverter
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs

Length of output: 1252

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (3)

5-5: LGTM: Import aligns with new JSON serialization support.

The addition of Newtonsoft.Json import is appropriate for the new JSON serialization attributes.


77-77: 🛠️ Refactor suggestion

Remove all null checks from operators.

As previously noted in past reviews, the null check patterns are inconsistent. However, since PixelBackground is a struct, all null checks in the operators are unnecessary and should be removed entirely.

 public static bool operator ==(PixelBackground left, PixelBackground right)
 {
-    if ((object)left is null) return (object)right is null;
     return left.Equals(right);
 }

 public static bool operator !=(PixelBackground left, PixelBackground right)
 {
-    if ((object)left == null) return (object)right != null;
     return !left.Equals(right);
 }

Also applies to: 83-83


30-31: Verify ColorConverter implementation and registration.

The ColorConverter type is referenced but not visible in the provided context. Let's verify its implementation.

✅ Verification successful

ColorConverter implementation is correct and properly located

The ColorConverter is correctly implemented in the same namespace (Consolonia.Core.Drawing.PixelBufferImplementation) and handles both serialization and deserialization of Color values appropriately. Since it's using Newtonsoft.Json attributes, explicit registration isn't required as the converter will be automatically picked up through the JsonConverter attribute on the property.

  • The converter properly handles parsing color strings using Color.TryParse
  • Fallbacks to Colors.Transparent for invalid values
  • Serializes colors using their string representation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for ColorConverter implementation
ast-grep --pattern 'class ColorConverter : JsonConverter<Color> {
  $$$
}'

# Verify if it's properly registered in the JSON serialization setup
rg -l "JsonSerializerSettings|AddJsonConverter.*ColorConverter"

Length of output: 142


Script:

#!/bin/bash
# Search for ColorConverter class with a more flexible pattern
rg -l "ColorConverter"

# Search for any JSON converter related to Color
ast-grep --pattern 'class $_ : JsonConverter<Color>'

# Search for Color type usage and conversion
rg -A 3 "class.*ColorConverter"

Length of output: 836


Script:

#!/bin/bash
# Check the implementation of ColorConverter
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/ColorConverter.cs

# Check if there's any JSON serialization setup in the project
rg -A 3 "AddJsonOptions|ConfigureJson|JsonSerializerOptions"

Length of output: 821

src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (3)

34-48: Well-structured property implementation with proper serialization support

The properties are well-designed with:

  • Immutability through init accessors
  • Proper JSON serialization attributes
  • Clear default value handling

47-48: Verify the impact of TextDecoration type change

The change from TextDecorationCollection to TextDecorationLocation? simplifies the model but might affect existing functionality. Ensure all consumers of this API are updated accordingly.

#!/bin/bash
# Search for usages of TextDecorations to ensure they're all updated
ast-grep --pattern 'TextDecorations'

# Search for any remaining TextDecorationCollection usage
rg "TextDecorationCollection" --type cs

Line range hint 11-12: Verify the intended type: struct or class?

The AI summary indicates this type should be converted from a struct to a class, but the code still shows it as a readonly struct. This inconsistency needs to be resolved as it significantly impacts memory allocation and usage patterns.

src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (3)

72-80: Well-designed static properties and IsEmpty implementation

The addition of Empty and Space static properties with clear documentation improves the API's usability. The IsEmpty() method's implementation correctly checks the symbol width.

Also applies to: 142-145


13-13: Maintaining struct definition is the right choice

Keeping Pixel as a struct is appropriate given its usage patterns in performance-critical rendering operations. This avoids the heap allocation and GC pressure issues that would arise from converting it to a class.


41-42: Verify the impact of constructor changes

Two significant changes to note:

  1. The textDecorations parameter type change from TextDecorationCollection to TextDecorationLocation? might affect existing callers
  2. The background-only constructor now explicitly uses a space character instead of an empty symbol

Also applies to: 52-54

✅ Verification successful

Let me gather more information about the text decoration usage to fully verify the impact.


Let me try one more search with a different approach to find any text decoration usage.


Based on the search results, I can now provide a final response:

Constructor changes are safe to proceed

  1. The change from TextDecorationCollection to TextDecorationLocation? is safe as there are no existing usages of the constructor with TextDecorationCollection. All existing code already uses TextDecorationLocation?.
  2. The background-only constructor with space character change is also safe, as the existing usages (new Pixel(new PixelBackground(...))) don't rely on the symbol parameter.

Key findings:

  • All text decoration usage in tests and implementation code already uses TextDecorationLocation?
  • The background-only constructor is used in tests and DrawingContextImpl.cs, but none of these usages are affected by the space character change
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing usages of these constructors
rg -tcs "new Pixel\([^)]*TextDecorationCollection" 
rg -tcs "new Pixel\(new PixelBackground"

Length of output: 695


Script:

#!/bin/bash
# Search for TextDecorationCollection usage in the codebase
rg -tcs "TextDecorationCollection"

# Search for all Pixel constructor calls with text decorations
rg -tcs "new Pixel\([^)]*textDecorations:"

# Check PixelForeground constructor calls
rg -tcs "new PixelForeground\([^)]*textDecorations:"

Length of output: 134


Script:

#!/bin/bash
# Search for any mentions of TextDecoration in the codebase
rg -tcs "TextDecoration" -A 2 -B 2

# Look for Pixel constructor calls with more context
rg -tcs "new Pixel\(" -A 3 -B 3

Length of output: 46714

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (8)
src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (1)

46-46: Consider making Width a computed property

Since Width is always derived from Text and is ignored in serialization, it could be simplified to a computed property.

Here's a suggested implementation:

-[JsonIgnore] public ushort Width { get; init; }
+[JsonIgnore] public ushort Width => Text.MeasureText();

This would:

  • Eliminate the need for Width initialization in constructors
  • Ensure Width is always consistent with Text
  • Remove the need for the init accessor
🧰 Tools
🪛 GitHub Check: build

[notice] 46-46:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs(46,49)

src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (1)

Line range hint 153-163: Remove unnecessary null-forgiving operator

Since DrawingBoxSymbol is a struct, the null-forgiving operator in the inequality operator is unnecessary.

Apply this diff to simplify the operator:

public static bool operator !=(DrawingBoxSymbol left, DrawingBoxSymbol right)
{
-    return !left!.Equals(right);
+    return !left.Equals(right);
}
src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (2)

72-88: LGTM! Well-documented static properties with clear purposes

The new static properties and init-only instance properties are well-designed:

  • ✅ Clear documentation explaining Empty vs Space
  • ✅ Immutable design with init accessors
  • ✅ Proper JsonIgnore attribute on computed property

Consider adding XML documentation for the public properties to improve IntelliSense experience.

🧰 Tools
🪛 GitHub Check: build

[notice] 85-85:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs(85,36)


139-142: Consider caching IsEmpty result

The IsEmpty method relies on Width which is already a computed property. Consider caching this value or making it a computed property for consistency:

-public bool IsEmpty()
-{
-    return Foreground.Symbol.Width == 0;
-}
+[JsonIgnore]
+public bool IsEmpty => Width == 0;
src/Consolonia.Core/Drawing/RenderTarget.cs (1)

87-95: Consider performance optimization and fix typo

The cache initialization could be optimized, and there's a typo in the comment.

Consider:

  1. Using Array.Fill or similar for better performance
  2. Fixing the typo in "initalize" -> "initialize"
 private static Pixel?[,] InitializeCache(ushort width, ushort height)
 {
     var cache = new Pixel?[width, height];
-    // initalize the cache with Pixel.Empty as it literally means nothing
-    for (ushort y = 0; y < height; y++)
-    for (ushort x = 0; x < width; x++)
-        cache[x, y] = Pixel.Empty;
+    // initialize the cache with Pixel.Empty as it literally means nothing
+    for (int i = 0; i < cache.Length; i++)
+        cache[i % width, i / width] = Pixel.Empty;
     return cache;
 }
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

527-583: Consider extracting complex pixel handling logic

The wide character and empty pixel handling logic is complex and could benefit from extraction into helper methods for improved readability and maintainability.

Consider extracting the following operations into helper methods:

  1. Empty pixel handling (lines 537-548)
  2. Wide character reset logic (lines 549-563)
  3. Wide character overlap handling (lines 565-577)

Example refactor:

+private void HandleEmptyPixel(Point characterPoint)
+{
+    if (characterPoint.X - 1 >= 0)
+        _pixelBuffer.Set(
+            (PixelBufferCoordinate)new Point(characterPoint.X - 1, characterPoint.Y),
+            oldPixel2 =>
+                new Pixel(
+                    new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
+                    oldPixel2.Background));
+}

+private void ResetOverlappedSymbols(Point characterPoint, ushort width)
+{
+    for (ushort i = 1; i < width; i++)
+    {
+        double targetX = characterPoint.X + i;
+        if (targetX < _pixelBuffer.Size.Width)
+            _pixelBuffer.Set(
+                (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
+                oldPixel2 =>
+                    new Pixel(
+                        new PixelForeground(new SimpleSymbol(' '),
+                            Colors.Transparent), oldPixel2.Background));
+    }
+}
src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (2)

445-446: Remove redundant parentheses in conditional statements

The parentheses in the conditional expression at lines 445-446 are unnecessary and can be removed to improve code readability.

Apply this diff to remove the redundant parentheses:

- else if ((x >= left+1 && x < right - 1) ||
-          (y >= top+1 && y < bottom-1))
+ else if (x >= left + 1 && x < right - 1 ||
+          y >= top + 1 && y < bottom - 1)
🧰 Tools
🪛 GitHub Check: build

[notice] 445-445:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(445,30)


[notice] 446-446:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(446,30)


541-542: Remove redundant parentheses in conditional statements

The parentheses in the conditional expression at lines 541-542 are unnecessary and can be removed to improve code readability.

Apply this diff to remove the redundant parentheses:

- else if ((x >= left + 1 && x < right - 1) ||
-          (y >= top + 1 && y < bottom - 1))
+ else if (x >= left + 1 && x < right - 1 ||
+          y >= top + 1 && y < bottom - 1)
🧰 Tools
🪛 GitHub Check: build

[notice] 541-541:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(541,30)


[notice] 542-542:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(542,30)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f39c232 and 67e29c3.

📒 Files selected for processing (8)
  • src/Consolonia.Core/Drawing/DrawingContextImpl.cs (7 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (3 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (7 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs (2 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs (4 hunks)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (2 hunks)
  • src/Consolonia.Core/Drawing/RenderTarget.cs (11 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBackground.cs
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelForeground.cs
🧰 Additional context used
🪛 GitHub Check: build
src/Consolonia.Core/Drawing/DrawingContextImpl.cs

[warning] 307-307:
"[CompareOfFloatsByEqualityOperator] Equality comparison of floating point numbers. Possible loss of precision while rounding values" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/DrawingContextImpl.cs(307,17)


[warning] 307-307:
"[CompareOfFloatsByEqualityOperator] Equality comparison of floating point numbers. Possible loss of precision while rounding values" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/DrawingContextImpl.cs(307,56)

src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs

[notice] 25-25:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs(25,44)


[warning] 29-29:
"[ConditionIsAlwaysTrueOrFalse] Expression is always false" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs(29,17)


[warning] 29-29:
"[HeuristicUnreachableCode] Code is heuristically unreachable" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs(29,40)

src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs

[notice] 85-85:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs(85,36)

src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs

[notice] 46-46:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs(46,49)

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs

[notice] 445-445:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(445,30)


[notice] 446-446:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(446,30)


[notice] 541-541:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(541,30)


[notice] 542-542:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(542,30)


[warning] 334-334:
"[RedundantAssignment] Value assigned is not used in any execution path" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(334,13)


[warning] 335-335:
"[RedundantAssignment] Value assigned is not used in any execution path" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(335,13)


[warning] 330-330:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(330,25)


[warning] 331-331:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(331,26)


[warning] 371-371:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(371,25)


[warning] 372-372:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(372,26)

🔇 Additional comments (33)
src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs (3)

16-18: LGTM! Improved empty symbol initialization

The initialization of Width to 0 for empty symbols is more logical, and the comment clearly documents the empty symbol representation.


39-42: LGTM! Efficient equality implementation

The Equals implementation correctly uses ordinal string comparison and follows best practices for value type equality.


6-6: LGTM! Verify SymbolConverter implementation

The addition of JSON serialization support is well-structured, using a custom converter for proper symbol handling.

Let's verify the SymbolConverter implementation:

Also applies to: 11-11

✅ Verification successful

JSON serialization implementation is correct and well-structured

The SymbolConverter implementation properly handles SimpleSymbol serialization:

  • Correctly serializes the Text property while ignoring the computed Width property
  • Handles both reading and writing of SimpleSymbol instances
  • Properly implements type checking and conversion
  • Follows best practices with proper null handling and type safety
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if SymbolConverter handles all SimpleSymbol properties correctly
ast-grep --pattern 'class SymbolConverter : JsonConverter<SimpleSymbol>
{
  $$$
  WriteJson($$$) {
    $$$
  }
  
  ReadJson($$$) {
    $$$
  }
  $$$
}'

Length of output: 154


Script:

#!/bin/bash
# Let's first find the SymbolConverter file
fd -e cs "SymbolConverter"

# Then let's check for any JSON converter implementations in the codebase
rg -l "JsonConverter.*SimpleSymbol" --type cs

# Let's also check for any usage of SimpleSymbol with JSON serialization
rg "JsonConvert.*SimpleSymbol" --type cs

Length of output: 190


Script:

#!/bin/bash
# Let's examine the SymbolConverter implementation
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/SymbolConverter.cs

# Also check SimpleSymbol class to understand what properties need serialization
cat src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs

Length of output: 3286

src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (4)

12-13: LGTM: Good improvements to immutability and serialization support

The changes to make this a readonly struct and add JSON serialization support are well-aligned with the PR's objectives for designer tooling integration.


21-25: LGTM: Property initialization looks good

The init-only property with constructor initialization is the correct approach here, maintaining immutability while allowing construction.

🧰 Tools
🪛 GitHub Check: build

[notice] 25-25:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs(25,44)


33-35: LGTM: Appropriate use of JsonIgnore attributes

The JsonIgnore attributes are correctly applied to properties that shouldn't be part of the serialized form.


37-53: LGTM: Well-structured method implementations

The methods are well-implemented with appropriate null handling and type checking. The use of the property instead of a field maintains encapsulation.

src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs (4)

13-14: Verify the intended type: struct vs class

There's an inconsistency between the code (which shows struct) and the AI-generated summary (which claims conversion to class). This is a critical architectural decision that significantly impacts performance and usage patterns.

If keeping as struct (current code):

  • ✅ Better performance for value-type semantics
  • ✅ Less garbage collection pressure
  • ✅ More efficient in arrays and collections

If converting to class (as per summary):

  • ❌ Increased heap allocations
  • ❌ More garbage collection pressure
  • ❌ Changed equality semantics

Line range hint 169-185: LGTM! Proper equality implementation

The equality implementation follows best practices:

  • ✅ Proper override of Equals and GetHashCode
  • ✅ Consistent operator overloads
  • ✅ Correct use of NotNullWhen attribute

41-43: Review constructor changes for potential breaking changes

Two significant changes in constructors:

  1. textDecorations parameter type changed from TextDecorationCollection to TextDecorationLocation?
  2. Background-only constructor now uses space character instead of empty symbol

While these changes improve the API:

  • ✅ More specific decoration type
  • ✅ Consistent width behavior with space character
  • ❌ Potential breaking change for existing callers

Run the following script to identify potential impact:

Also applies to: 52-54

✅ Verification successful

Let me analyze the usage of both types to verify if this is a breaking change.


No breaking changes detected - API change is safe

The change from TextDecorationCollection to TextDecorationLocation? is safe because:

  1. No existing code uses TextDecorationCollection with Pixel constructors
  2. All current usages already use TextDecorationLocation? throughout the codebase
  3. The change aligns with the existing implementation in PixelForeground which already uses TextDecorationLocation?

The space character change for background-only pixels is also safe as it's an internal implementation detail that doesn't affect the public API surface.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for existing usages of these constructors
rg -A 2 'new Pixel\(' --type cs

# Search for TextDecorationCollection usage with Pixel
rg 'TextDecorationCollection.*new Pixel' --type cs

Length of output: 11884


Script:

#!/bin/bash
# Search for TextDecorationLocation usage in Pixel-related code
rg -A 2 'TextDecorationLocation' --type cs

# Search for any TextDecorationCollection usage
rg -A 2 'TextDecorationCollection' --type cs

Length of output: 9356


Line range hint 95-168: LGTM! Well-structured blending logic

The color blending implementation is robust with:

  • ✅ Clear mode-based branching
  • ✅ Proper alpha blending calculation
  • ✅ Good separation of concerns

Verify the blending logic is tested with edge cases:

✅ Verification successful

LGTM! Blending implementation is well-tested

The blending logic has comprehensive test coverage including:

  • ✅ Transparent background blending
  • ✅ Colored background blending
  • ✅ Complex foreground blending with font styles
  • ✅ Unicode/emoji character blending
  • ✅ Color merging scenarios

The implementation is robust and thoroughly verified through tests.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for tests covering blend operations
rg -l 'Blend.*test' --type cs
rg -l 'MergeColors.*test' --type cs

Length of output: 69


Script:

#!/bin/bash
# Let's try a broader search for test files related to Pixel class
fd ".*test.*\.cs$" -I | xargs rg -l "Pixel"

# Also search for any test files in the test directories
fd -I -g "*test*.cs" -p "test"

# Search for any usage of Blend method to understand test coverage
rg "\.Blend\(" --type cs

Length of output: 3527


Script:

#!/bin/bash
# Let's examine the test cases in PixelTests.cs to verify coverage of blending scenarios
rg -A 10 "Blend" src/Tests/Consolonia.Core.Tests/PixelTests.cs

# Also check PixelForegroundTests.cs for blend-related tests
rg -A 10 "Blend" src/Tests/Consolonia.Core.Tests/PixelForegroundTests.cs

Length of output: 3860

src/Consolonia.Core/Drawing/RenderTarget.cs (3)

30-30: LGTM: Cache initialization is properly handled

The cache initialization is correctly implemented using the PixelBuffer dimensions.


100-100: LGTM: Rendering logic is properly implemented

The direct PixelBuffer access and loop control are correctly handled.

Also applies to: 134-134


164-164: LGTM: FlushingBuffer changes are well implemented

The changes properly handle:

  • Text decoration using nullable TextDecorationLocation
  • Width checks for empty pixels with clear documentation
  • Updated Print parameters

Also applies to: 184-184, 193-193, 197-202, 212-212

src/Consolonia.Core/Drawing/DrawingContextImpl.cs (2)

37-41: LGTM: Improved encapsulation in constructor

The constructor now properly accesses PixelBuffer through ConsoleWindow instead of requiring it as a separate parameter, which reduces coupling and improves encapsulation.


104-104: LGTM: Consistent pixel blending implementation

The pixel blending operations have been simplified and made consistent across different methods (DrawBitmap, DrawRectangle, and tab handling), improving code maintainability.

Also applies to: 178-179, 511-511

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (17)

35-51: Test BufferInitialized is correctly implemented

The test method BufferInitialized properly checks the initial state of the PixelBuffer, ensuring that all pixels are initialized with the correct default values.


53-65: Test DrawText is comprehensive and accurate

The DrawText test effectively verifies that single-character text is drawn correctly at specified positions, and the assertions confirm the expected pixel properties.


67-87: Test DrawSingleWide functions as expected

The DrawSingleWide test correctly assesses the drawing of single-width characters, including overwriting a character and verifying pixel properties.


89-118: Test DrawDoubleWide is correctly verifying double-width character rendering

The DrawDoubleWide test accurately checks the handling of double-width characters and ensures that the subsequent pixels are properly managed.


120-150: Test DrawOverDoubleWideFirstChar handles overwriting double-width characters

The test method effectively validates the behavior when overwriting the first part of a double-width character, ensuring correct pixel updates.


152-183: Test DrawOverDoubleWideSecondChar correctly handles overwriting the second part of a double-width character

This test ensures that overwriting the second half of a double-width character updates the pixels appropriately.


186-203: Test DrawLineStrikethrough properly verifies text decoration

The DrawLineStrikethrough test accurately checks the application of strikethrough text decoration over existing text, with correct assertions on pixel properties.


205-222: Test DrawLineUnderline effectively tests underline decoration

The test successfully verifies the application of underline text decoration and ensures pixel properties are as expected.


225-244: Test DrawHorizontalLine correctly draws horizontal lines

The DrawHorizontalLine test method accurately tests the drawing of horizontal lines and includes proper assertions for pixel symbols and colors.


246-266: Test DrawVerticalLine correctly draws vertical lines

This test method effectively verifies the drawing of vertical lines and correctly asserts the pixel properties.


268-317: Test DrawLinesCrossingMakeCross robustly tests line intersections

The DrawLinesCrossingMakeCross test thoroughly checks the rendering of intersecting horizontal and vertical lines, ensuring correct symbols are used at crossings.


319-358: Test DrawSolidRectangle accurately tests filled rectangle rendering

The test properly verifies that a solid rectangle is drawn with the correct background color and that pixels outside the rectangle remain unaffected.

🧰 Tools
🪛 GitHub Check: build

[warning] 334-334:
"[RedundantAssignment] Value assigned is not used in any execution path" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(334,13)


[warning] 335-335:
"[RedundantAssignment] Value assigned is not used in any execution path" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(335,13)


[warning] 330-330:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(330,25)


[warning] 331-331:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(331,26)


360-453: Test DrawSingleBox effectively validates box drawing with single lines

The DrawSingleBox test method correctly tests the drawing of a box with single-line borders and includes comprehensive assertions for all relevant pixels.

🧰 Tools
🪛 GitHub Check: build

[notice] 445-445:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(445,30)


[notice] 446-446:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(446,30)


[warning] 371-371:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(371,25)


[warning] 372-372:
"[UselessBinaryOperation] Addition or subtraction of 0 in every execution path, which is useless" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(372,26)


455-549: Test DrawDoubleBox successfully verifies double-line box drawing

This test accurately checks the rendering of boxes with double-line borders, ensuring that corner and side characters are correctly placed and that colors are properly applied.

🧰 Tools
🪛 GitHub Check: build

[notice] 541-541:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(541,30)


[notice] 542-542:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(542,30)


551-570: Method DrawText is properly implemented

The DrawText method correctly handles text shaping and rendering using Avalonia's services. The implementation is precise and efficient.


572-576: Method SetOrigin correctly adjusts the drawing context transformation

The SetOrigin method effectively updates the transformation matrix to set the drawing origin, aiding in accurate rendering positions.


577-608: Dispose pattern is correctly implemented

The Dispose methods appropriately manage resource cleanup, following the recommended disposal pattern in .NET. The inclusion of comments for potential future enhancements is noted.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (3)

13-13: Consider removing or documenting the pragma directive.

The #pragma warning disable CA1305 suppresses culture-specific string operations warnings. Instead of suppressing the warning globally, consider:

  1. Using culture-specific overloads where needed
  2. Adding a comment explaining why the warning is suppressed

590-592: Address TODO comments in disposal implementation.

The TODO comments indicate incomplete implementation of unmanaged resource cleanup. Since this is a test class, it's unlikely to have unmanaged resources. Consider either:

  1. Removing the TODO comments if no unmanaged resources are used
  2. Implementing the unmanaged resource cleanup if needed

21-22: Consider adding edge case tests.

The test suite is comprehensive but could benefit from additional test cases:

  1. Test with empty or whitespace-only text
  2. Test with very long text that exceeds buffer boundaries
  3. Test with various Unicode characters including emojis and RTL text
  4. Test with zero-width/zero-height rectangles
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 67e29c3 and 8976e1d.

📒 Files selected for processing (1)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (5)
src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (5)

47-50: Use Assert.AreEqual for clearer test assertions

Using Assert.AreEqual instead of Assert.IsTrue for equality checks provides more informative failure messages and improves test readability.

Apply this diff to improve the assertions:

-                        Assert.IsTrue(pixel.Width == 1);
+                        Assert.AreEqual(1, pixel.Width);
-                        Assert.IsTrue(pixel.Foreground.Symbol.Text == " ");
+                        Assert.AreEqual(" ", pixel.Foreground.Symbol.Text);
-                        Assert.IsTrue(pixel.Foreground.Color == Colors.Transparent);
+                        Assert.AreEqual(Colors.Transparent, pixel.Foreground.Color);
-                        Assert.IsTrue(pixel.Background.Color == Colors.Transparent);
+                        Assert.AreEqual(Colors.Transparent, pixel.Background.Color);

63-65: Use Assert.AreEqual for equality checks

For consistency and better failure diagnostics, replace Assert.IsTrue with Assert.AreEqual in your assertions.

Apply this diff:

-                    Assert.IsTrue(buffer[x, 2].Width == 1);
+                    Assert.AreEqual(1, buffer[x, 2].Width);
-                    Assert.IsTrue(buffer[x, 2].Foreground.Symbol.Text == x.ToString());
+                    Assert.AreEqual(x.ToString(), buffer[x, 2].Foreground.Symbol.Text);

79-82: Refine assertions with Assert.AreEqual

Replacing Assert.IsTrue with Assert.AreEqual enhances test clarity and provides more detailed failure information.

Apply this diff:

-                        Assert.IsTrue(buffer[x, 1].Width == 1);
+                        Assert.AreEqual(1, buffer[x, 1].Width);
-                        Assert.IsTrue(buffer[x, 1].Foreground.Symbol.Text == "X");
+                        Assert.AreEqual("X", buffer[x, 1].Foreground.Symbol.Text);
-                        Assert.IsTrue(buffer[x, 1].Foreground.Color == Colors.Blue);
+                        Assert.AreEqual(Colors.Blue, buffer[x, 1].Foreground.Color);

447-448: Remove redundant parentheses in conditional expression

The parentheses in the condition are unnecessary and can be removed to improve code readability.

Apply this diff to remove the redundant parentheses:

-                        else if ((x >= left + 1 && x < right - 1) ||
-                                 (y >= top + 1 && y < bottom - 1))
+                        else if x >= left + 1 && x < right - 1 ||
+                                 y >= top + 1 && y < bottom - 1
🧰 Tools
🪛 GitHub Check: build

[notice] 447-447:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(447,30)


[notice] 448-448:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(448,30)


543-544: Remove redundant parentheses in conditional expression

Similarly, these parentheses are redundant and can be omitted for clarity.

Apply this diff:

-                        else if ((x >= left + 1 && x < right - 1) ||
-                                 (y >= top + 1 && y < bottom - 1))
+                        else if x >= left + 1 && x < right - 1 ||
+                                 y >= top + 1 && y < bottom - 1
🧰 Tools
🪛 GitHub Check: build

[notice] 543-543:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(543,30)


[notice] 544-544:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(544,30)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8976e1d and 4408182.

📒 Files selected for processing (2)
  • src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (3 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)
🧰 Additional context used
🪛 GitHub Check: build
src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs

[notice] 25-25:
"[AutoPropertyCanBeMadeGetOnly.Global] Auto-property can be made get-only" on /home/runner/work/Consolonia/Consolonia/src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs(25,44)

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs

[notice] 447-447:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(447,30)


[notice] 448-448:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(448,30)


[notice] 543-543:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(543,30)


[notice] 544-544:
"[ArrangeRedundantParentheses] Redundant parentheses" on /home/runner/work/Consolonia/Consolonia/src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs(544,30)

🔇 Additional comments (5)
src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs (4)

4-4: LGTM: JSON serialization support and immutability improvements

The addition of JSON serialization support and readonly struct modifier enhances the type's functionality while maintaining immutability.

Also applies to: 12-13


21-22: LGTM: Property initialization follows immutable design

The init-only property and constructor initialization align well with the immutable design pattern.

Also applies to: 25-25


32-52: LGTM: Well-structured property and method implementations

The JsonIgnore attributes are correctly applied to derived properties, and the Blend method properly handles different symbol types with appropriate bit operations.


27-31: 🛠️ Refactor suggestion

Remove unnecessary null check in Equals method

Since DrawingBoxSymbol is a struct, it cannot be null. The null-forgiving operator (!) is unnecessary.

Apply this diff:

public bool Equals(DrawingBoxSymbol other)
{
-    return UpRightDownLeft == other!.UpRightDownLeft;
+    return UpRightDownLeft == other.UpRightDownLeft;
}
src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1)

559-561: Handle potential failure when loading the font

As previously noted, if the 'Cascadia Mono' font is unavailable, typeface may be null, leading to exceptions. Please add proper handling for this scenario.

tomlm and others added 2 commits November 19, 2024 21:39
Co-authored-by:  <+@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (4)
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (1)

528-581: Consider extracting pixel handling logic into separate methods

The text rendering logic handles multiple complex scenarios but could be more maintainable if broken down into smaller, focused methods.

Consider refactoring like this:

-if (oldPixel.Width == 0)
-{
-    // if the oldPixel was empty, we need to set the previous pixel to space
-    double targetX = characterPoint.X - 1;
-    if (targetX >= 0)
-        _pixelBuffer.Set(
-            (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
-            oldPixel2 =>
-                new Pixel(
-                    new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
-                    oldPixel2.Background));
-}
+private void HandleEmptyPixel(Point characterPoint)
+{
+    double targetX = characterPoint.X - 1;
+    if (targetX >= 0)
+        _pixelBuffer.Set(
+            (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
+            oldPixel2 =>
+                new Pixel(
+                    new PixelForeground(new SimpleSymbol(' '), Colors.Transparent),
+                    oldPixel2.Background));
+}

-else if (oldPixel.Width > 1)
-{
-    // if oldPixel was wide we need to reset overlapped symbols from empty to space
-    for (ushort i = 1; i < oldPixel.Width; i++)
-    {
-        double targetX = characterPoint.X + i;
-        if (targetX < _pixelBuffer.Size.Width)
-            _pixelBuffer.Set(
-                (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
-                oldPixel2 =>
-                    new Pixel(
-                        new PixelForeground(new SimpleSymbol(' '),
-                            Colors.Transparent), oldPixel2.Background));
-    }
-}
+private void HandleWidePixel(Point characterPoint, ushort width)
+{
+    for (ushort i = 1; i < width; i++)
+    {
+        double targetX = characterPoint.X + i;
+        if (targetX < _pixelBuffer.Size.Width)
+            _pixelBuffer.Set(
+                (PixelBufferCoordinate)new Point(targetX, characterPoint.Y),
+                oldPixel2 =>
+                    new Pixel(
+                        new PixelForeground(new SimpleSymbol(' '),
+                            Colors.Transparent), oldPixel2.Background));
+    }
+}

This would make the code more readable and easier to maintain while keeping the same functionality.

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (3)

582-613: Clean up Dispose method by removing unnecessary comments and code.

The Dispose pattern implementation contains TODO comments and commented-out code related to the finalizer. Since there are no unmanaged resources being used, the finalizer and related comments can be removed to improve code clarity.

Apply this diff to clean up the Dispose method:

             _disposedValue = true;
         }
     }
- 
-     // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
-     // ~DrawingContextImplTests()
-     // {
-     //     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
-     //     Dispose(disposing: false);
-     // }
 
     public void Dispose()
     {
         // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
         Dispose(true);
         GC.SuppressFinalize(this);
     }

46-51: Use more specific assertions to improve test diagnostics.

The tests use Assert.IsTrue with conditions, which may not provide detailed information when a test fails. Consider using more specific assertions like Assert.AreEqual or adding custom messages to make debugging easier.

Example:

- Assert.IsTrue(pixel.Width == 1);
- Assert.IsTrue(pixel.Foreground.Symbol.Text == " ");
- Assert.IsTrue(pixel.Foreground.Color == Colors.Transparent);
- Assert.IsTrue(pixel.Background.Color == Colors.Transparent);
+ Assert.AreEqual(1, pixel.Width, $"Expected pixel width to be 1 at position ({x}, {y})");
+ Assert.AreEqual(" ", pixel.Foreground.Symbol.Text, $"Expected symbol ' ' at position ({x}, {y})");
+ Assert.AreEqual(Colors.Transparent, pixel.Foreground.Color, $"Expected transparent foreground color at position ({x}, {y})");
+ Assert.AreEqual(Colors.Transparent, pixel.Background.Color, $"Expected transparent background color at position ({x}, {y})");

55-88: Replace magic numbers with named constants for better readability.

Using hard-coded numbers like 1, 5, and 10 in the tests can make the code less readable and harder to maintain. Defining these values as named constants or variables can improve code clarity.

Apply this diff to define constants:

+ const ushort startX = 1;
+ const ushort endX = 6;
+ const ushort yPosition = 2;

- for (ushort x = 1; x < 6; x++) DrawText(dc, x, 2, x.ToString(), Brushes.White);
- for (ushort x = 1; x < 6; x++)
+ for (ushort x = startX; x < endX; x++) DrawText(dc, x, yPosition, x.ToString(), Brushes.White);
+ for (ushort x = startX; x < endX; x++)
 {
     Assert.IsTrue(buffer[x, yPosition].Width == 1);
     Assert.IsTrue(buffer[x, yPosition].Foreground.Symbol.Text == x.ToString());
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 4408182 and 1f07aee.

📒 Files selected for processing (3)
  • src/Consolonia.Core/Drawing/DrawingContextImpl.cs (7 hunks)
  • src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1 hunks)
  • src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Tests/Consolonia.Core.Tests/PixelBackgroundTests.cs
🔇 Additional comments (5)
src/Consolonia.Core/Drawing/DrawingContextImpl.cs (4)

37-41: LGTM: Improved encapsulation in constructor

The constructor changes improve encapsulation by accessing PixelBuffer through ConsoleWindow instead of injecting it directly.


104-104: LGTM: Simplified pixel blending

The pixel blending operation has been simplified while maintaining the same functionality.


178-179: LGTM: Consistent pixel blending implementation

The pixel blending changes in DrawRectangle maintain consistency with other similar operations in the codebase.


213-214: LGTM: Improved null safety

Good use of ArgumentNullException.ThrowIfNull for null checking the shapedBuffer.

src/Tests/Consolonia.Core.Tests/DrawingContextImplTests.cs (1)

555-564: ⚠️ Potential issue

Handle potential failure when loading the font.

Currently, the code assumes that the 'Cascadia Mono' font is available and installed on the system. If the font is not found, TryCreateGlyphTypeface will return false, and typeface will be null, leading to an ArgumentNullException. To enhance robustness, consider handling the case where the font is unavailable and provide a fallback or a clear error message.

Apply this diff to handle the potential failure:

- fontManager.TryCreateGlyphTypeface("Cascadia Mono", FontStyle.Normal, FontWeight.Normal, FontStretch.Normal,
-     out IGlyphTypeface typeface);
- ArgumentNullException.ThrowIfNull(typeface);
+ if (!fontManager.TryCreateGlyphTypeface("Cascadia Mono", FontStyle.Normal, FontWeight.Normal, FontStretch.Normal,
+     out IGlyphTypeface typeface))
+ {
+     throw new InvalidOperationException("Failed to load 'Cascadia Mono' font. Ensure the font is installed.");
+ }

Likely invalid or redundant comment.

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.

1 participant