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

Merged
merged 70 commits into from
Nov 30, 2024
Merged

Live preview while editing AXAML files #150

merged 70 commits into from
Nov 30, 2024

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 DummyConsole for basic console functionality.
    • Added ConsolePreview for rendering console AXAML outputs in Visual Studio.
    • Implemented JSON serialization for DrawingBoxSymbol, Pixel, PixelForeground, and SimpleSymbol.
    • New Fluent and Material themes added for enhanced UI styling.
  • Improvements

    • Enhanced pixel manipulation and rendering logic in DrawingContextImpl.
    • Updated project dependencies and improved theme management.
    • Streamlined application startup process with better command-line argument handling.
  • Bug Fixes

    • Refined equality checks and null handling in various classes.
  • Documentation

    • Updated project files and added launch settings for better configuration.

Copy link
Contributor

coderabbitai bot commented Nov 17, 2024

Walkthrough

The pull request introduces significant changes across multiple files in the Consolonia project, including the addition of new classes, modifications to existing methods, and updates to project configurations. Key changes include the introduction of JSON serialization for various classes, updates to the handling of text decorations, and enhancements to the drawing context. Additionally, new project files for the Consolonia.Designer and Avalonia.PreviewHost are created, along with modifications to existing test files to ensure proper functionality and coverage.

Changes

File Path Change Summary
src/Consolonia.Core/Assembly.cs Added assembly attribute [assembly: InternalsVisibleTo("Consolonia.Designer")].
src/Consolonia.Core/Consolonia.Core.csproj Removed package references for Avalonia.FreeDesktop and SkiaSharp.NativeAssets.Linux.NoDependencies; added Avalonia.ReactiveUI and Newtonsoft.Json.
src/Consolonia.Core/Drawing/DrawingContextImpl.cs Updated constructor to accept only ConsoleWindow; simplified pixel blending logic in DrawBitmap and DrawStringInternal.
src/Consolonia.Core/Drawing/PixelBufferImplementation/ColorConverter.cs Added ColorConverter class for JSON serialization of Color.
src/Consolonia.Core/Drawing/PixelBufferImplementation/DrawingBoxSymbol.cs Changed DrawingBoxSymbol to a readonly struct; added JSON serialization attributes.
src/Consolonia.Core/Drawing/PixelBufferImplementation/ISymbol.cs Added [JsonConverter(typeof(SymbolConverter))] to ISymbol interface.
src/Consolonia.Core/Drawing/PixelBufferImplementation/Pixel.cs Converted 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 Updated PixelBuffer class with new constructors and indexers; added JSON serialization attributes.
src/Consolonia.Core/Drawing/PixelBufferImplementation/PixelBufferConverter.cs Added PixelBufferConverter class for custom JSON serialization of PixelBuffer.
src/Consolonia.Core/Drawing/PixelBufferImplementation/SimpleSymbol.cs Changed SimpleSymbol from struct to class; added JSON serialization attributes.
src/Consolonia.Core/Drawing/RenderTarget.cs Modified pixel buffer management and rendering logic; updated method signatures.
src/Consolonia.Core/Dummy/DummyConsole.cs Introduced DummyConsole class for basic console functionality.
src/Consolonia.Core/Helpers/Extensions.cs Updated Print and MeasureText methods for new text decoration handling.
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs Changed class visibility to public; added PixelBuffer property.
src/Consolonia.Core/Infrastructure/ConsoloniaApplication.cs Added OnFrameworkInitializationCompleted method for design-time styling.
src/Consolonia.Core/Infrastructure/IConsole.cs Updated Print method signature; added WriteText method.
src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs Enhanced console output handling and emoji support.
src/Consolonia.Core/Text/Esc.cs Refactored ConsoleUtils to Esc with 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 AXAML previews.
src/Consolonia.Designer/Consolonia.Designer.csproj Created new project file for Consolonia.Designer.
src/Consolonia.Gallery/App.cs Updated theme management in App class.
src/Consolonia.Gallery/Consolonia.Gallery.csproj Modified project configuration for resource inclusion.
src/Consolonia.Gallery/Gallery/GalleryViews/*.axaml Added XML namespaces for design-time support across multiple XAML files.
src/Consolonia.Gallery/Gallery/GalleryViews/ResourceTest.axaml Introduced new resource dictionary for design-time previews.
src/Tests/Consolonia.Core.Tests/*.cs Added new test methods for JSON serialization across various classes.
src/Tools/Avalonia.PreviewHost/*.cs Introduced new files for Avalonia preview host application structure and functionality.
src/Tools/Consolonia.PreviewHost/*.csproj Created project file for Consolonia.PreviewHost.

Possibly related PRs

  • Bitmap support #127: The main PR introduces an assembly attribute for internal visibility, which may relate to the bitmap support changes in this PR that enhance graphics rendering capabilities, potentially requiring access to internal members.
  • Add Italic, strikethrough and fix line draw bug  #133: This PR modifies the DrawingContextImpl class, which is directly related to the changes in the main PR that enhance visibility for the Consolonia.Designer assembly, as both involve drawing and rendering functionalities.
  • Fix strikethrough implementation #134: The updates to the DrawingContextImpl class regarding strikethrough and underline functionalities are relevant to the main PR's focus on enhancing internal member visibility for better text rendering capabilities.
  • Add support for Surrogate Unicode sequences  #139: The changes in DrawingContextImpl to support surrogate Unicode sequences are related to the main PR's goal of improving the interaction between Consolonia.Core and Consolonia.Designer, particularly in rendering text and symbols.
  • Enable alternate screen buffer #141: The modifications to enable the alternate screen buffer in the ConsoloniaLifetime class may relate to the main PR's changes that enhance the integration between the core and designer assemblies, improving overall console functionality.

Suggested reviewers

  • jinek
  • goe5mu

Poem

🐰 In the garden of code, we hop and play,
With every change, we brighten the day.
New colors and symbols, all in a line,
Together we build, making our designs shine!
So let’s celebrate with a joyful cheer,
For Consolonia's changes, we hold so dear! 🐇

Warning

Rate limit exceeded

@tomlm has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ff89176 and 94844a3.


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.

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 (1)
src/Consolonia.Designer/ConsolePreview.cs (1)

271-304: Consider optimizing pixel buffer rendering for performance

The RenderPixelBuffer method creates a StackPanel with nested TextBlock elements for each line and character, which may have performance implications for large buffers due to the UI elements' overhead.

Consider using a more efficient approach, such as rendering the entire text content in a single TextBlock or leveraging a DrawingContext to draw text directly. This can improve rendering performance and reduce memory usage.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 2af4047 and ab5c6ca.

📒 Files selected for processing (1)
  • src/Consolonia.Designer/ConsolePreview.cs (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/Consolonia.Designer/ConsolePreview.cs (1)
Learnt from: tomlm
PR: jinek/Consolonia#150
File: src/Consolonia.Designer/ConsolePreview.cs:161-173
Timestamp: 2024-11-29T19:09:22.038Z
Learning: In C#, the `File.ReadAllText` method throws an `IOException`, not a `FileNotFoundException`. Therefore, catching `IOException` is sufficient to handle scenarios where a file is not found or cannot be accessed.
🔇 Additional comments (6)
src/Consolonia.Designer/ConsolePreview.cs (6)

331-345: Guard against division by zero when computing character dimensions

In the ComputeCharWidth method, if _charWidth and _charHeight remain zero after attempting to measure text, default values are assigned. However, if the measurement fails and the defaults are also zero (though currently they are set to 10 and 20), this could lead to division by zero elsewhere in the code.

Ensure that _charWidth and _charHeight are never zero after this method to prevent potential runtime errors.

Verify that the default values ensure non-zero dimensions:

 if (_charWidth == 0)
-    _charWidth = 10;
+    _charWidth = 10; // Ensure this is a non-zero default
 if (_charHeight == 0)
-    _charHeight = 20;
+    _charHeight = 20; // Ensure this is a non-zero default

48-57: ⚠️ Potential issue

Avoid wrapping essential functionality within #if DEBUG directives

The initialization code, including the assignment of _process, font settings, and event subscriptions to Initialized and PropertyChanged, is enclosed within #if DEBUG directives. This means that in Release builds, critical functionality will be omitted, potentially leading to a non-functional control outside of Debug configurations.

Consider removing the #if DEBUG directives to ensure that the control works correctly in all build configurations.

Apply this diff to adjust the code:

-#if DEBUG
     _process = null;
     FontFamily = FontFamily.Parse("Cascadia Mono");
     Initialized += (_, _) => LoadXaml();

     PropertyChanged += (_, e) =>
     {
         if (e.Property == FileNameProperty) LoadXaml();
     };
-#endif

98-119: ⚠️ Potential issue

Ensure process resources are properly released in all build configurations

In the Dispose(bool disposing) method, the code that disposes of _process is wrapped inside #if DEBUG directives. This means that in Release builds, any process started by ConsolePreview may not be properly terminated and disposed of, potentially leading to resource leaks.

Consider removing the #if DEBUG directives around the disposal code to ensure _process is properly disposed in all build configurations.

Apply this diff to adjust the code:

 protected void Dispose(bool disposing)
 {
     if (!_disposedValue)
     {
         if (disposing)
         {
             // Dispose managed state (managed objects)
-#if DEBUG
 #pragma warning disable CA1416 // Validate platform compatibility
             if (_process != null)
             {
                 _process.Kill();
                 _process.Dispose();
                 _process = null;
             }
 #pragma warning restore CA1416 // Validate platform compatibility
-#endif
         }

         _disposedValue = true;
     }
 }

347-366: ⚠️ Potential issue

Add validation to prevent exceptions when parsing design width and height

In the GetDesignWidthAndHeight method, if the expected patterns are not found, IndexOf may return -1, leading to invalid indices and potential exceptions when using Substring. It's important to validate iStart and iEnd before attempting to extract substrings.

Apply this diff to add validation:

 int iStart = xaml.IndexOf("d:DesignWidth=\"", StringComparison.Ordinal);
 if (iStart > -1)
 {
     iStart += 15;
     int iEnd = xaml.IndexOf("\"", iStart, StringComparison.Ordinal);
+    if (iEnd > iStart)
+    {
         string num = xaml.Substring(iStart, iEnd - iStart);
         _ = ushort.TryParse(num, out designWidth);
+    }
 }

 iStart = xaml.IndexOf("d:DesignHeight=\"", StringComparison.Ordinal);
+if (iStart > -1)
 {
     iStart += 16;
     int iEnd = xaml.IndexOf("\"", iStart, StringComparison.Ordinal);
+    if (iEnd > iStart)
+    {
         string num = xaml.Substring(iStart, iEnd - iStart);
         _ = ushort.TryParse(num, out designHeight);
+    }
 }

228-230: ⚠️ Potential issue

Add null-check for _process before writing to StandardInput

When sending a request to the preview host to load the new XAML file, ensure that _process is not null and has not exited to prevent a potential NullReferenceException or writing to a closed stream.

Apply this diff to add a null-check:

 // Send request to previewHost to load the new xaml file.
+if (_process != null && !_process.HasExited)
+{
     _process.StandardInput.WriteLine(xamlPath);
+}
+else
+{
+    Debug.WriteLine("Process is not running. Cannot send XAML path.");
+}

Likely invalid or redundant comment.


122-126: ⚠️ Potential issue

Implement the IDisposable interface explicitly

The ConsolePreview class contains Dispose methods but does not implement the IDisposable interface. This may lead to unexpected behavior when disposing of the object, as the dispose pattern relies on the interface to signal that resources should be released.

Implement the IDisposable interface to follow .NET best practices for resource management.

Apply this diff to implement IDisposable:

-public class ConsolePreview : UserControl
+public class ConsolePreview : UserControl, IDisposable
 {
     // Class implementation
 }

 public void Dispose()
 {
     // Dispose of managed resources.
     Dispose(true);
+    GC.SuppressFinalize(this);
 }

Likely invalid or redundant comment.

src/Consolonia.Designer/ConsolePreview.cs Show resolved Hide resolved
src/Consolonia.Designer/ConsolePreview.cs Outdated Show resolved Hide resolved
@jinek
Copy link
Owner

jinek commented Nov 29, 2024

Lets disable CA1303? @tomlm
I would do in a separate PR

jinek and others added 3 commits November 30, 2024 13:51
# Conflicts:
#	src/Consolonia.Core/Consolonia.Core.csproj
#	src/Consolonia.Core/Infrastructure/ConsoleWindow.cs
#	src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml
#	src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs
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 (4)
src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs (1)

79-84: Add parameter validation for text shaping

While the implementation is correct, it would benefit from parameter validation to ensure robust error handling.

Consider adding these validations:

+if (string.IsNullOrEmpty(value))
+{
+    _shapedText = null;
+    return;
+}
+
ShapedBuffer glyphs =
    textShaper.ShapeText(value.AsMemory(),
        new TextShaperOptions(typeface, typeface.Metrics.DesignEmHeight));

+if (glyphs.Length == 0)
+{
+    throw new InvalidOperationException("Text shaping produced no glyphs.");
+}
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (2)

21-21: Consider adding null checks and documentation for the public API

The class is now public and introduces a new PixelBuffer property. Since this is part of the public API:

  1. Consider adding XML documentation to describe the class's purpose and the property's usage
  2. Add null checks for the PixelBuffer initialization to prevent potential null reference exceptions
+ /// <summary>
+ /// Implements window functionality for console-based applications.
+ /// </summary>
 public class ConsoleWindow : IWindowImpl
 {
     public ConsoleWindow()
     {
         // ... existing code ...
-        PixelBuffer = new PixelBuffer(Console.Size);
+        PixelBuffer = new PixelBuffer(Console.Size ?? throw new InvalidOperationException("Console size cannot be null"));
     }

+    /// <summary>
+    /// Gets or sets the pixel buffer used for console window rendering.
+    /// </summary>
     public PixelBuffer PixelBuffer { get; set; }

Also applies to: 40-40, 43-43


Line range hint 243-254: Consider using pattern matching for feature type checks

The current implementation could be more maintainable using pattern matching and a dictionary of known types.

 public object TryGetFeature(Type featureType)
 {
-    if (featureType == typeof(ISystemNavigationManagerImpl))
-        return null;
-    if (featureType == typeof(ITextInputMethodImpl)) return null;
-    if (featureType == typeof(IStorageProvider))
-        return AvaloniaLocator.Current.GetService<IStorageProvider>();
-    throw new NotImplementedException("Consider this");
+    return featureType switch
+    {
+        Type t when t == typeof(ISystemNavigationManagerImpl) => null,
+        Type t when t == typeof(ITextInputMethodImpl) => null,
+        Type t when t == typeof(IStorageProvider) => AvaloniaLocator.Current.GetService<IStorageProvider>(),
+        _ => throw new NotImplementedException($"Feature type {featureType.Name} is not supported")
+    };
 }
src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (1)

Line range hint 107-131: Optimize string concatenation performance

The current implementation creates multiple string concatenations through StringBuilder. Consider pre-calculating the capacity and chaining the append calls.

-            var sb = new StringBuilder();
+            var sb = new StringBuilder(str.Length + 50);  // Estimate for escape sequences
             if (textDecoration == TextDecorationLocation.Underline)
-                sb.Append(Esc.Underline);
+                sb.Append(Esc.Underline)
+                  .Append(Esc.Background(background))
+                  .Append(Esc.Foreground(weight switch
+                  {
+                      FontWeight.Medium or FontWeight.SemiBold or FontWeight.Bold or FontWeight.ExtraBold or FontWeight.Black
+                          or FontWeight.ExtraBlack
+                          => foreground.Brighten(background),
+                      FontWeight.Thin or FontWeight.ExtraLight or FontWeight.Light
+                          => foreground.Shade(background),
+                      _ => foreground
+                  }))
+                  .Append(str)
+                  .Append(Esc.Reset);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ab5c6ca and 02f31b1.

📒 Files selected for processing (11)
  • src/Consolonia.Core/Consolonia.Core.csproj (1 hunks)
  • src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (6 hunks)
  • src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (8 hunks)
  • src/Consolonia.Gallery/App.cs (3 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml.cs (1 hunks)
  • src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml (1 hunks)
  • src/Consolonia.Gallery/View/ControlsListView.axaml.cs (1 hunks)
  • src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs (3 hunks)
  • src/Tests/Consolonia.Core.Tests/Consolonia.Core.Tests.csproj (1 hunks)
  • src/Tools/Avalonia.PreviewHost/Avalonia.PreviewHost.csproj (1 hunks)
  • src/Tools/Consolonia.PreviewHost/Consolonia.PreviewHost.csproj (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/Tools/Consolonia.PreviewHost/Consolonia.PreviewHost.csproj
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/Consolonia.Gallery/Gallery/GalleryViews/SomeDialogWindow.axaml
  • src/Tests/Consolonia.Core.Tests/Consolonia.Core.Tests.csproj
  • src/Consolonia.Gallery/Gallery/GalleryViews/GalleryWelcome.axaml.cs
  • src/Consolonia.Gallery/App.cs
  • src/Consolonia.Core/Consolonia.Core.csproj
  • src/Consolonia.Gallery/View/ControlsListView.axaml.cs
  • src/Tools/Avalonia.PreviewHost/Avalonia.PreviewHost.csproj
🔇 Additional comments (8)
src/Consolonia.Themes.TurboVision/Templates/Controls/Helpers/SymbolsControl.cs (4)

103-103: LGTM: Pattern matching improves readability

The use of pattern matching with is not { Length: > 0 } makes the code more expressive and maintainable.


72-74: ⚠️ Potential issue

Add null checks for Avalonia services

Services obtained from AvaloniaLocator.Current.GetService<T>() might return null if not registered. This could lead to runtime exceptions.

Apply this diff to add null checks:

var platformRender = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
+ if (platformRender == null)
+     throw new InvalidOperationException("IPlatformRenderInterface service is not available.");

var textShaper = AvaloniaLocator.Current.GetService<ITextShaperImpl>();
+ if (textShaper == null)
+     throw new InvalidOperationException("ITextShaperImpl service is not available.");

var fontManager = AvaloniaLocator.Current.GetService<IFontManagerImpl>();
+ if (fontManager == null)
+     throw new InvalidOperationException("IFontManagerImpl service is not available.");

75-77: ⚠️ Potential issue

Improve GlyphTypeface creation error handling

The TryCreateGlyphTypeface method's boolean result is ignored, and the error message doesn't clearly indicate the actual issue when the font creation fails.

Apply this diff to improve error handling:

-fontManager.TryCreateGlyphTypeface("Cascadia Mono", FontStyle.Normal, FontWeight.Normal,
-    FontStretch.Normal, out IGlyphTypeface typeface);
-ArgumentNullException.ThrowIfNull(typeface);
+bool success = fontManager.TryCreateGlyphTypeface("Cascadia Mono", FontStyle.Normal, FontWeight.Normal,
+    FontStretch.Normal, out IGlyphTypeface typeface);
+if (!success || typeface == null)
+{
+    throw new InvalidOperationException("Failed to create GlyphTypeface for 'Cascadia Mono'. Ensure the font is installed.");
+}

105-112: 🛠️ Refactor suggestion

Optimize text rendering performance

The current implementation creates a new FormattedText instance for each line, which could be inefficient for large heights.

Consider caching the FormattedText:

-for (int y = 0; y < Bounds.Height; y++)
-    context.DrawText(formattedText, new Point(0, y));
+var formattedTextCache = formattedText;
+for (int y = 0; y < Bounds.Height; y++)
+    context.DrawText(formattedTextCache, new Point(0, y));

Also, verify the MeasureText extension method:

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

390-409: ⚠️ Potential issue

Complete the implementation of resource disposal

The Dispose pattern implementation still contains TODO comments and might miss disposing of some managed resources. Consider:

  1. Disposing of _resizeCancellationTokenSource
  2. Disposing of PixelBuffer if it implements IDisposable
  3. Removing the TODO comments once implemented
 protected virtual void Dispose(bool disposing)
 {
     if (!_disposedValue)
     {
         if (disposing)
         {
-            // TODO: dispose managed state (managed objects)
             Closed?.Invoke();
             Console.Resized -= OnConsoleOnResized;
             Console.KeyEvent -= ConsoleOnKeyEvent;
             Console.MouseEvent -= ConsoleOnMouseEvent;
             Console.FocusEvent -= ConsoleOnFocusEvent;
             Console.Dispose();
+            _resizeCancellationTokenSource?.Dispose();
+            (PixelBuffer as IDisposable)?.Dispose();
         }

-        // TODO: free unmanaged resources (unmanaged objects) and override finalizer
-        // TODO: set large fields to null
+        PixelBuffer = null;
         _disposedValue = true;
     }
 }
src/Consolonia.Core/Infrastructure/InputLessDefaultNetConsole.cs (3)

50-72: ⚠️ Potential issue

Improve reliability of emoji support detection

The current implementation has several potential issues:

  1. Writing to console during detection may cause visual artifacts
  2. Using cursor position for detection might be unreliable across different terminal emulators
  3. Defaulting to true on failure could lead to incorrect behavior
  4. The TODO comment suggests incomplete implementation

Consider this alternative approach that uses buffer width checking:

 public bool SupportsComplexEmoji
 {
     get
     {
         if (_supportEmoji == null)
         {
-            // Detect complex emoji support by writing a complex emoji and checking cursor position.
-            // If the cursor moves 2 positions, it indicates proper rendering of composite surrogate pairs.
-            (int left, int top) = Console.GetCursorPosition();
-            WriteText(
-                $"{Esc.Foreground(Colors.Transparent)}{Esc.Background(Colors.Transparent)}{TestEmoji}");
-
-            // TODO, escape sequence
-            (int left2, _) = Console.GetCursorPosition();
-            _supportEmoji = left2 - left == 2;
-            Console.SetCursorPosition(left, top);
-
-            WriteText(Esc.ClearScreen);
+            try
+            {
+                var testStr = TestEmoji;
+                _supportEmoji = Console.BufferWidth >= testStr.Length * 2;
+            }
+            catch (Exception)
+            {
+                _supportEmoji = false;  // Safer default
+            }
         }

-        return _supportEmoji ?? true;
+        return _supportEmoji ?? false;  // Safer default
     }
 }

164-165: 🛠️ Refactor suggestion

Ensure reliable console state restoration

The current implementation might leave the console in an inconsistent state if an exception occurs during cleanup.

Wrap the console state restoration in a try-finally block:

-            WriteText(Esc.DisableAlternateBuffer);
-            WriteText(Esc.ShowCursor);
+            try
+            {
+                WriteText(Esc.DisableAlternateBuffer);
+                WriteText(Esc.ShowCursor);
+            }
+            finally
+            {
+                // Ensure we never leave the console in an inconsistent state
+            }

141-145: ⚠️ Potential issue

Avoid virtual method in constructor chain

The WriteText method is called from PrepareConsole which is invoked in the constructor. This is dangerous as derived classes may override WriteText before their constructors are called.

Consider either:

  1. Making WriteText non-virtual
  2. Moving console preparation to an Initialize method called after construction
-        public virtual void WriteText(string str)
+        protected internal void WriteText(string str)
         {
             PauseTask?.Wait();
             Console.Write(str);
         }

Likely invalid or redundant comment.

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 (3)
src/Consolonia.Designer/ConsolePreview.cs (3)

253-254: Fix typo in comment

There's a minor typo in the comment: "we continue to check the proces." The word "proces" should be "process".


269-269: Address the TODO: Use theme background brush

The comment indicates that the Background should use the theme's background brush instead of a hardcoded color. This improves consistency with the application's theme and user settings.

Would you like assistance in implementing this change?


371-371: Address the TODO: Move TextBlockComposer class out

The TextBlockComposer class has a TODO comment suggesting it should be moved out. To improve code organization and maintainability, consider moving this nested class to a separate file or refactoring it as appropriate.

Would you like assistance in refactoring this class?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fae1776 and b7d976d.

📒 Files selected for processing (1)
  • src/Consolonia.Designer/ConsolePreview.cs (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/Consolonia.Designer/ConsolePreview.cs (1)
Learnt from: tomlm
PR: jinek/Consolonia#150
File: src/Consolonia.Designer/ConsolePreview.cs:161-173
Timestamp: 2024-11-29T19:09:22.038Z
Learning: In C#, the `File.ReadAllText` method throws an `IOException`, not a `FileNotFoundException`. Therefore, catching `IOException` is sufficient to handle scenarios where a file is not found or cannot be accessed.
🔇 Additional comments (4)
src/Consolonia.Designer/ConsolePreview.cs (4)

5-455: Reconsider extensive use of #if DEBUG directives affecting functionality

A significant portion of the code, including essential methods like LoadXaml(), ListenForChanges(), and the _process field, is wrapped within #if DEBUG directives. This means that in Release builds, critical functionality of the ConsolePreview control will be excluded, potentially leading to runtime issues or a non-functional control outside of Debug configurations.

Consider the following actions:

  • Refactor the code to minimize the use of #if DEBUG directives, ensuring that necessary functionality is included in all build configurations.
  • Implement design-time checks using Design.IsDesignMode to distinguish between runtime and design-time environments instead of relying solely on compilation symbols.
  • Document the intended behavior clearly if the control is meant to function only in Debug mode.

98-126: ⚠️ Potential issue

Implement the IDisposable interface

The ConsolePreview class defines Dispose() and Dispose(bool disposing) methods but does not implement the IDisposable interface. This may lead to unexpected behavior when the object is disposed, as the dispose pattern relies on the interface to signal that resources should be released.

Apply this diff to implement IDisposable:

-public class ConsolePreview : UserControl
+public class ConsolePreview : UserControl, IDisposable
{
    // Class implementation
}

+public void Dispose()
+{
+    Dispose(true);
+    GC.SuppressFinalize(this);
+}

161-179: 🛠️ Refactor suggestion

Simplify exception handling by catching only IOException

File.ReadAllText throws IOException for most file access errors, including file not found and unauthorized access. Simplifying the exception handling by catching only IOException makes the code cleaner and more maintainable.

Apply this diff to adjust the exception handling:

try
{
    xaml = File.ReadAllText(xamlPath);
    ArgumentNullException.ThrowIfNull(xaml);
}
- catch (FileNotFoundException ex)
- {
-     Content = new TextBlock { Text = $"XAML file not found. {ex.Message}", Foreground = Brushes.Red };
-     return;
- }
- catch (UnauthorizedAccessException ex)
- {
-     Content = new TextBlock { Text = $"Unable to access XAML file. {ex.Message}", Foreground = Brushes.Red };
-     return;
- }
catch (IOException ex)
{
    Content = new TextBlock { Text = $"Unable to load XAML file. {ex.Message}", Foreground = Brushes.Red };
    return;
}

348-360: ⚠️ Potential issue

Add validation to prevent exceptions when parsing design dimensions

In the GetDesignWidthAndHeight method, if the expected patterns d:DesignWidth or d:DesignHeight are not found, IndexOf may return -1, leading to ArgumentOutOfRangeException when using Substring. Adding validation ensures that iStart and iEnd are valid before attempting to extract substrings.

Apply this diff to add validation:

int iStart = xaml.IndexOf("d:DesignWidth=\"", StringComparison.Ordinal);
if (iStart > -1)
{
    iStart += 15;
    int iEnd = xaml.IndexOf("\"", iStart, StringComparison.Ordinal);
+   if (iEnd > iStart)
+   {
        string num = xaml.Substring(iStart, iEnd - iStart);
        _ = ushort.TryParse(num, out designWidth);
+   }
}

iStart = xaml.IndexOf("d:DesignHeight=\"", StringComparison.Ordinal);
if (iStart > -1)
{
    iStart += 16;
    int iEnd = xaml.IndexOf("\"", iStart, StringComparison.Ordinal);
+   if (iEnd > iStart)
+   {
        string num = xaml.Substring(iStart, iEnd - iStart);
        _ = ushort.TryParse(num, out designHeight);
+   }
}

@jinek
Copy link
Owner

jinek commented Nov 30, 2024

Lets disable CA1303? @tomlm I would do in a separate PR

I've opened a separate PR for this. Will cleanup this code after in that same PR.
For this one the only question from my side is this one: #150 (comment)

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 (3)
src/Consolonia.Designer/ConsolePreview.cs (3)

253-253: Correct typographical error in comment

There is a typo in the comment: "we continue to check the proces." It should be "we continue to check the process."

Apply this diff to fix the typo:

- // process was probably shut down, we continue to check the proces.
+ // Process was probably shut down; we continue to check the process.

371-371: Address TODO: Move TextBlockComposer class to a separate file

The TextBlockComposer nested class is marked with a TODO comment to move it out. For better code organization and maintainability, consider moving it to its own file.

Would you like assistance in refactoring TextBlockComposer into a separate file?


249-249: Use InvokeAsync instead of Invoke to prevent UI blocking

When updating the UI, using Dispatcher.UIThread.Invoke can block the UI thread. Consider using InvokeAsync to ensure responsive UI.

Apply this diff:

- Dispatcher.UIThread.Invoke(() => Content = RenderPixelBuffer(buffer));
+ await Dispatcher.UIThread.InvokeAsync(() => Content = RenderPixelBuffer(buffer));

This change updates the UI asynchronously, improving application responsiveness.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between b7d976d and 6c131a6.

📒 Files selected for processing (1)
  • src/Consolonia.Designer/ConsolePreview.cs (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/Consolonia.Designer/ConsolePreview.cs (1)
Learnt from: tomlm
PR: jinek/Consolonia#150
File: src/Consolonia.Designer/ConsolePreview.cs:161-173
Timestamp: 2024-11-29T19:09:22.038Z
Learning: In C#, the `File.ReadAllText` method throws an `IOException`, not a `FileNotFoundException`. Therefore, catching `IOException` is sufficient to handle scenarios where a file is not found or cannot be accessed.
🔇 Additional comments (6)
src/Consolonia.Designer/ConsolePreview.cs (6)

46-58: ⚠️ Potential issue

Beware of functionality exclusion due to #if DEBUG directives

The constructor and several critical methods are wrapped within #if DEBUG directives. This means that in Release builds, essential functionalities like process initialization and event subscriptions will be excluded, potentially leading to a non-functional control.

Consider refactoring the code to minimize the use of #if DEBUG directives. Use Design.IsDesignMode to check for design-time environment instead of compilation symbols.


98-118: ⚠️ Potential issue

Ensure proper disposal by implementing IDisposable interface

The ConsolePreview class defines a Dispose method but does not implement the IDisposable interface. This may lead to improper disposal of resources and unmanaged processes.

Apply this diff to implement the IDisposable interface:

-public class ConsolePreview : UserControl
+public class ConsolePreview : UserControl, IDisposable

Also, ensure to call GC.SuppressFinalize(this); in the Dispose() method to prevent the finalizer from running:

 public void Dispose()
 {
     // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
     Dispose(true);
+    GC.SuppressFinalize(this);
 }

164-179: 🛠️ Refactor suggestion

Simplify exception handling by catching only IOException

In the LoadXaml method, catching FileNotFoundException and UnauthorizedAccessException separately is unnecessary because File.ReadAllText throws an IOException for these errors.

Apply this diff to simplify the exception handling:

 try
 {
     xaml = File.ReadAllText(xamlPath);
     ArgumentNullException.ThrowIfNull(xaml);
 }
-catch (FileNotFoundException ex)
-{
-    Content = new TextBlock { Text = $"XAML file not found. {ex.Message}", Foreground = Brushes.Red };
-    return;
-}
-catch (UnauthorizedAccessException ex)
-{
-    Content = new TextBlock { Text = $"Unable to access XAML file. {ex.Message}", Foreground = Brushes.Red };
-    return;
-}
 catch (IOException ex)
 {
     Content = new TextBlock { Text = $"Unable to load XAML file. {ex.Message}", Foreground = Brushes.Red };
     return;
 }

347-361: ⚠️ Potential issue

Add validation to prevent exceptions when parsing design dimensions

In GetDesignWidthAndHeight, if the expected patterns are not found, IndexOf may return -1, leading to exceptions when using Substring.

Apply this diff to add necessary validation:

 int iStart = xaml.IndexOf("d:DesignWidth=\"", StringComparison.Ordinal);
-if (iStart > 0)
+if (iStart >= 0)
 {
     iStart += 15;
     int iEnd = xaml.IndexOf("\"", iStart, StringComparison.Ordinal);
+    if (iEnd > iStart)
+    {
         string num = xaml.Substring(iStart, iEnd - iStart);
         _ = ushort.TryParse(num, out designWidth);
+    }
 }

+iStart = xaml.IndexOf("d:DesignHeight=\"", StringComparison.Ordinal);
+if (iStart >= 0)
+{
+    iStart += 16;
+    int iEnd = xaml.IndexOf("\"", iStart, StringComparison.Ordinal);
+    if (iEnd > iStart)
+    {
+        string num = xaml.Substring(iStart, iEnd - iStart);
+        _ = ushort.TryParse(num, out designHeight);
+    }
+}

This adds checks to ensure indices are valid before attempting to parse substrings.


236-262: ⚠️ Potential issue

Handle potential exceptions when reading from standard output

In the ListenForChanges method, reading from _process.StandardOutput may throw exceptions if the process exits unexpectedly.

Apply this diff to add exception handling:

 while (true)
 {
     if (_process == null || _process.HasExited)
         return;

-    string? line = await _process!.StandardOutput.ReadLineAsync().ConfigureAwait(false);
+    string? line;
+    try
+    {
+        line = await _process!.StandardOutput.ReadLineAsync().ConfigureAwait(false);
+    }
+    catch (Exception ex)
+    {
+        Debug.WriteLine($"Error reading from process output: {ex.Message}");
+        return;
+    }

     if (string.IsNullOrEmpty(line)) continue;

     // Rest of the code...
 }

This ensures that any exceptions during the read operation are caught and handled gracefully.

Likely invalid or redundant comment.


282-291: ⚠️ Potential issue

Prevent potential infinite loop due to zero-width symbols

In the loop handling Pixel objects, a zero Symbol.Width could lead to an infinite loop.

Apply this diff to ensure that widthAdjust is always at least 1:

 ushort widthAdjust = pixel.Foreground.Symbol.Width == 0 ? (ushort)1 : pixel.Foreground.Symbol.Width;
+if (widthAdjust == 0)
+{
+    widthAdjust = 1;
+}
 x += widthAdjust;

This ensures that the loop progresses and avoids the risk of an infinite loop.

Likely invalid or redundant comment.

src/Consolonia.Designer/ConsolePreview.cs Show resolved Hide resolved
jinek
jinek previously approved these changes Nov 30, 2024
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 (2)
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (2)

21-21: Consider documenting the public API surface

The class has been made public, which expands the API surface. Consider adding XML documentation to describe the class's purpose, responsibilities, and usage guidelines.


256-261: Decide on finalizer implementation

The commented-out finalizer suggests uncertainty about unmanaged resource handling. Either:

  1. Remove the commented code if no unmanaged resources are present
  2. Implement the finalizer if unmanaged resources need cleanup
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 6c131a6 and ff89176.

📒 Files selected for processing (1)
  • src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (6 hunks)
🔇 Additional comments (3)
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (3)

316-318: Consider thread-safety when writing to System.Console

Direct writes to System.Console might not be thread-safe. Consider using the injected IConsole interface instead.


337-339: Handle resource cleanup during PixelBuffer reassignment

When creating a new PixelBuffer, ensure the old instance is properly disposed if it implements IDisposable.


391-410: ⚠️ Potential issue

Complete resource cleanup in Dispose method

The Dispose method needs attention in several areas:

  1. Add disposal of PixelBuffer in the disposing block
  2. Remove TODO comments after implementing resource cleanup
  3. Consider disposing _resizeCancellationTokenSource
                 if (disposing)
                 {
-                    // TODO: dispose managed state (managed objects)
                     Closed?.Invoke();
                     Console.Resized -= OnConsoleOnResized;
                     Console.KeyEvent -= ConsoleOnKeyEvent;
                     Console.MouseEvent -= ConsoleOnMouseEvent;
                     Console.FocusEvent -= ConsoleOnFocusEvent;
+                    (PixelBuffer as IDisposable)?.Dispose();
+                    _resizeCancellationTokenSource?.Dispose();
                     Console.Dispose();
                 }

-                // TODO: free unmanaged resources (unmanaged objects) and override finalizer
-                // TODO: set large fields to null
                 _disposedValue = true;

Likely invalid or redundant comment.

@tomlm
Copy link
Collaborator Author

tomlm commented Nov 30, 2024

GAH. I believed coderabbit that it was dispossable.
I am so filled with shame and regret!

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 (2)
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (2)

21-21: Document public API surface

The class has been made public, which exposes it as part of the public API. Consider adding XML documentation to describe its purpose, usage, and any threading considerations.


396-396: Address remaining TODO comments in Dispose method

The Dispose method contains unaddressed TODO comments. Please:

  1. Document any managed resources that need disposal
  2. Document any unmanaged resources that need cleanup
  3. Remove the TODO comments once addressed

This is important for proper resource management and preventing memory leaks.

Also applies to: 405-406

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ff89176 and 94844a3.

📒 Files selected for processing (1)
  • src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (6 hunks)
🔇 Additional comments (3)
src/Consolonia.Core/Infrastructure/ConsoleWindow.cs (3)

43-43: Protect PixelBuffer from unauthorized modifications

The public setter on PixelBuffer could lead to thread-safety issues and invalid state.


316-318: Consider thread-safety when writing to System.Console

Direct writes to System.Console might not be thread-safe.


337-338: Handle resource cleanup during PixelBuffer reassignment

When creating a new PixelBuffer, ensure the old instance is properly disposed.

@tomlm
Copy link
Collaborator Author

tomlm commented Nov 30, 2024

I need you to approve again. this is what you already approved,

@tomlm tomlm merged commit bfae13f into main Nov 30, 2024
2 checks passed
@tomlm tomlm deleted the tomlm/preview branch November 30, 2024 20:10
@tomlm
Copy link
Collaborator Author

tomlm commented Nov 30, 2024

W00t!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants