AppoMobi.Maui.DrawnUi 1.0.1.11-pre

This is a prerelease version of AppoMobi.Maui.DrawnUi.
There is a newer version of this package available.
See the version list below for details.
dotnet add package AppoMobi.Maui.DrawnUi --version 1.0.1.11-pre                
NuGet\Install-Package AppoMobi.Maui.DrawnUi -Version 1.0.1.11-pre                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AppoMobi.Maui.DrawnUi" Version="1.0.1.11-pre" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add AppoMobi.Maui.DrawnUi --version 1.0.1.11-pre                
#r "nuget: AppoMobi.Maui.DrawnUi, 1.0.1.11-pre"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install AppoMobi.Maui.DrawnUi as a Cake Addin
#addin nuget:?package=AppoMobi.Maui.DrawnUi&version=1.0.1.11-pre&prerelease

// Install AppoMobi.Maui.DrawnUi as a Cake Tool
#tool nuget:?package=AppoMobi.Maui.DrawnUi&version=1.0.1.11-pre&prerelease                

AppoMobi.Maui.DrawnUi

Rendering engine for .Net MAUI to draw your UI on a skia canvas, with gestures, animations and more..

Supports iOS, MacCatalyst, Android, Windows.

  • To use inside a usual Maui app, consume drawn controls here and there inside Canvas views.
  • Create a totally drawn app with just one Canvas as root view and consume controls inside, SkiaShell is provided for navigation.

The current development state is PRE-ALPHA, some features remain to be implemented, the project is active. This readme is heavily under construction, a lot of properties and use cases are not yet documented.

Demo App

Available at: https://github.com/taublast/AppoMobi.Maui.DrawnUi.Demo

What's new

  • SkiaControl derives from Element.

Development Notes

Closest roadmap:

  • SkiaLabel to support tappable spans
  • FIX recyclable collectionview alternative to fully support got dynamic height cells (chat messages style), this way it will be production ready with header, footer, loadmore, refreshview, scrollto features.

Then finally go public and might ask for community help to fix hotrealod issues for .net 7

To notice:

  • Expect this repo to be updated very often and breaking changes to occure
  • Expect fps to double in Release builds (on Windows the lib fps in manually flex-capped at 200).
  • In the demo app you can return to root view inside same tab on reselecting its icon (the Fun screen has no GoBack button)

Maui limitations:

  • Visual Studio Xaml Hot-Reload working partially out-of-the-box, due to some Maui interfaces beign private and other internal logic, workarounds are to be implemented.

LayoutRounding and Pixel Snapping are auto-applied everywhere and are not optional. Using device independent unit as input everywhere, so it's up to controls to internally handle pixels dimensions properly to avoid falling between pixels.

Features

  • Draw your UI using SkiaSharp with hardware acceleration

  • Easily create your controls and animations

  • Design in Xaml or code-behind

  • 2D and 3D Transforms

  • Animations targeting max fps

  • Gestures support for panning, scrolling and zooming (rotation incoming)

  • Caching system for elements and images

  • Optimized for performance, rendering only visible elements, recycling templates etc

  • Prebuilt Basic Ui Elements

    • SkiaShape (Rounded rectangle, Circle, Gauge, more to come) can wrap other elements
    • SkiaLabel, multiline with many options
    • SkiaImage with options and filters
    • SkiaSvg with many options
    • SkiaLottie with tint customization
    • SkiaRive (todo ios and android)
    • SkiaLayout (Absolute, Grid, Vertical stack, Horizontal stack, todo Masonry) with templates support
    • SkiaScroll (Horizonal, Vertical, Both) with header, footer, zoom support and adjustable inertia, bounce, snap and much more
    • SkiaScroll + SkiaLayout = CollectionView-like control with refresh, custom refresh indicator, load more etc
    • SkiaHotspot to handle gestures in a easy way
    • SkiaMauiElement for when skia is not enough
  • Derived custom controls

    • SkiaButton include anything inside, text, images etc
    • SkiaScrollLooped for neverending scrolls
    • SkiaDrawer to swipe in and out your controls
    • SkiaCarousel swipe and slide controls inside a carousel
    • SkiaHoverMask to overlay a clipping shape
    • SkiaLabelFps for developement
    • SkiaDecoratedGrid to draw shapes between rows and columns
    • ScrollPickerWheel for creating wheel pickers
    • RefreshIndicator can use lottie and anything for your scroll refresh view
    • SkiaTabsSelector create top and bottom tabs
    • SkiaViewSwitcher switch your views, pop, push and slide
    • Create your own!
  • Animated Effects

    • Ripple
    • Shimmer
    • BlinkColors
    • (todo Pulse, Shake etc)
    • Commit yours!
  • Animators

    • _todo add info
  • Transforms

    • TranslationX
    • TranslationY
    • ScaleX
    • ScaleY
    • Rotation
    • CameraAngleX
    • CameraAngleY
    • CameraAngleZ
    • SkewX
    • SkewY
    • Perspective1
    • Perspective2

Installation

Install the package AppoMobi.Maui.DrawnUi from NuGet.

After that initialize the library inside your MauiProgram.cs file:

builder.UseDrawnUi<App>();

Quick Start

You will be mainly using Maui view Canvas that will wrap your SkiaControls. Anywhere in your existing Maui app you can include a Canvas and start drawing your UI. The Canvas control is aware of its children size and will resize accordingly. At the same time you could set a fixed size for the Canvas and its children will adapt to it.

Xaml

Import the namespace:

  xmlns:draw="http://schemas.appomobi.com/drawnUi/2023/draw"

Consume:

<draw:Canvas>
     <draw:SkiaSvg
        Source="Svg/dotnet_bot.svg"
        LockRatio="1"
		HorizontalOptions="Center"
        TintColor="White"
        WidthRequest="44" />
</draw:Canvas>

As you can see in this example the Maui view Canvas will adapt it's size to drawn content and should take 44x44 pts.

Code behind
	_todo_

Please check the demo app, it contains many examples of usage.

todo put important explanation about caching again

Gestures

To make your root Canvas catch gestures you need to attach a TouchEffect to it. After that skia controls can process gestures in multiple ways:

  • Implementing an ISkiaGestureListener interface and overriding OnGestureReceived.
  • Attaching a HandleGestures effect that has properties similar SkiaHotspot.
  • Including a SkiaHotspot as a child.
  • Using a SkiaButton.

Parent controls have full control over gestures and passing them to chidlren. In a base scenarion a gesture would be passed all along to the ends of a view tree to its ends for every top-level control. If a gesture is marked as consumed (by returning true) a control would typically stop processing gestures at this level.

By overrifing OnGestureReceived any control might process gestures with or without passing them to children.

When creating a custom control the standart code for the override would be to pass gestures below by caling base then processing at the current level. You might choose to do it differently acording your needs.

The engine is designed to pass the ending gestures to thoses who already returned "consumed" for preceding gestures even if following gestures are out of their hitbox.

When the DOWN gestures is received the engine will try to find the topmost control that can handle it. The overridable AutoHitbox would be checked for intersection with gesture, in base this hitbox is checking versus DrawingRect.

Avoid trying to handle gastures below the cached level, as DrawingRect might still point to older position in the screen if the cached parent already moved.

Caching System

! Without caching animations going beyond simple wouldn't be possible. Caching makes complicated processing needed just once for layout calculation and then the caching result is redrawn on every frame.

if crashing check: you cannot use hardware accelerated bitmap for sizes bigger than device screen you cannot record a hardware accelerated bitmap into an operations cache

Controls Rendering Result

Caching is controlled using a property UseCache of the following type:

public enum SkiaCacheType
{
    /// <summary>
    /// True and old school
    /// </summary>
    None,

    /// <summary>
    /// Create and reuse SKPicture. Try this first for labels, svg etc. 
    /// Do not use this when dropping shadows or with other effects, better use Bitmap. 
    /// </summary>
    Operations,

    /// <summary>
    /// Will use simple SKBitmap cache type, will not use hardware acceleration.
    /// Slower but will work for sizes bigger than graphics memory if needed.
    /// </summary>
    Image,

    /// <summary>
    /// The way to go when dealing with images surrounded by shapes etc.
    /// The cached surface will use the same graphic context as your canvas.
    /// If hardware acceleration is enabled will try to cache as Bitmap inside graphics memory. Will fallback to simple Bitmap cache type if not possible. If you experience issues using it, switch to Memory cache type.
    /// </summary>
    GPU,
}

You should tweak your design caching to avoid unnecessary re-drawing of elements. The basic approach here is to cache small elements at some level. For example you would cache small children like cells inside a SkiaScroll as Image. At the same time avoid using bitmaps where just Operations type is enough, for example for SkiaSvg results.

When you start using any kind of animations you should start using caching to max your FPS. You can check the DemoApp for such examples.

Loaded Images

We are using the EasyCaching.InMemory library for caching loaded bitmaps. It's impact can much be seen when using recycled cells inside a scroll. todo add options and link to ImageLoader and SkiaImage docs

! When using images inside dynamic scene, like a a templated stack with scroll or other you should try to set the image cache to Image this would most probably climb your fps. This is due the fact that image sources are usually of the wrong size and they need processing before being drawn. When using Image cache the image would be processed only once and then just redrawn.

Transforms

todo

  • TranslationX
  • TranslationY
  • ScaleX
  • ScaleY
  • Rotation
  • CameraAngleX
  • CameraAngleY
  • CameraAngleZ
  • SkewX
  • SkewY
  • Perspective1
  • Perspective2

Animations

todo

One would create animations and effects using animators. Animators are attached to controls, but technically register themselves at the root canvas level and their code is executed on every rendering frame. If the canvas is not redrawing then animators will not be executed. When the canvas has a registered animator running it would constantly force re-drawing itsself until all animators are stopped.

There are two types of animators:

  • Animators are executed before the drawing, so you can move and transform elements before the are rendered on the canvas.
  • PostAnimators are executed after their parent element was drawn, so you can paint an effect over the existing result, or execute any other post-drawing logic.

Hint: If you need to create a game loop you could put its code inside an animator and attach the animator with the root element as parent.

todo restore from deleted another way of running a game loop that was not redrawing canvas on every tick

Layout

For initial items positionning you would be using SkiaLayout. Its Absolute layout type is already buil-it in every skia control..

You can position your children using familiar properties like HorizonalOptions, VerticalOptions, Margin, parent Padding, WidthRequest, HeightRequest,MinimumWidthRequest, MinimumHeightRequest and additional MaximumWidthRequest, MaximumHeightRequest , HorizontalFillRatio, VerticalFillRatio, LockRatio properties.

For dynamic positionning or other precise cases use TranslationX and TranslationY.

Whn you need to layout children in a more arranged way you will want to wrap them with a SkiaLayout of different LayoutType : Grid, Colum, Row and others.

Layout supports ItemTemplate for most of layout types.

Some differences from Xamarin.Forms/Maui to notice:

  • Layouts do not implement the Expand feature, in order to limit number of calculation passes, so if your children do not request a size, the parent layout will not take any space if you do not ask it to Fill.

  • In Column and Row layouts we do not support Fill, End and Center for children, only Start, for the same reasons. When you need Fill, End and Center please use Absolute or Grid types of layout.

! Layouts Column and Row, watever templated or not, will always check if child out of the visible scrren bounds and avoid rendering it in that case. That is especialy useful when the layout is inside a SkiaScroll, this way we always render only the visible part.

! You can absolutely use the Margin property as usual in a Maui way. In case if you would need to bind a specific margin you can switch to using MarginLeft, MarginTop, MarginRight, MarginBottom properties, -1.0 by default, if set they will override specific value from Margin, and the result would be accessible via Margins readonly bindable static property. When designing custom controls please use Margins property to read the final margin value.

Loading sources

SkiaImage, SkiaLottie and other controls that have a Source property, can load data from web, from bundled resources and from native file system. The conventions is the following:

  • if a web url is detected the source is loaded from web
  • if a file path starts with file:// it will be loaded from native file system
  • otherwise will try to load from bundled resources with root folder 'Resources\Raw'.

Example below will load animation from Resources\Raw\Lottie\Loader.json.

            <draw:SkiaLottie
                InputTransparent="True"
                AutoPlay="True"
                ColorTint="{StaticResource ColorPrimary}"
                HorizontalOptions="Center"
                LockRatio="1"
                Opacity="0.85"
                Repeat="-1"
                Source="Lottie/Loader.json"
                Tag="Loader"
                VerticalOptions="Center"
                WidthRequest="56" />
Enhanced usage

When dealing with subviews in code behind you could typically use two ways.

Example for adding a subview:

  • Use method SetParent passing new parent. In this case parent layout will not be invalidated, use this for optimized logic when you know what you are doing. You can mainly use this way when just constructing parent, knowing it will be measured at start anyway.
  • Call parent's method AddSubView passing subview.Parent's layout will be invalidated, and OnChildAdded will be called on parent.
  • When working with Children property use Add method, it will set Views to a new instance of appropriate collection, and call AddSubView for each item.

For removing a subview the usual options would be:

  • Call SetParent passing null, for soft removal.
  • Call parent's method RemoveSubView passing subview. Parent's layout will be invalidated, and OnChildRemoved will be called on parent.
  • When working with Children property use Remove method, it will set Views to a new instance of appropriate collection, and call RemoveSubView for each item.
In Deep

When XAML would be constructed it would fill Children property with views, this property is for high-level access. Views

Will be then filled internally. When you add or remove items in Children methods AddSubView and RemoveSubView will be called for managing Views.

Enhanced Usage

Draw a line or rectangle or reserve space

Use a simple SkiaControl. For complex shapes use SkiaShape or SkiaPath.

Simulate Maui Grid

SkiaLayout of Grid type, set children properties as usual (Grid.something)

Simulate Maui CollectionView

SkiaScroll + SkiaLayout (ItemTemplate=...). Set cache of the cell to Bitmap or Operations depending on your needs.

Simulate Maui StackLayout with a BidableLayout.ItemTemplate

SkiaScroll (Virtualisation=false) + SkiaLayout (ItemTemplate=..., UseCache=CacheType.Operations)

Styles

When you want to dynamically change properties in Xaml you might want to use conditional styles. They look like regular Maui styles, but with some nuances:

  • When defining style is resources you must set a unique Class attibute
  • They are selected at runtime upon Condition or State bindable properties. State is like Maui VisualStatebut you can have several of them applied at same time.

Define a style inside ResourceDictionary:

    <Style
        x:Key="SkiaLabelDefaultStyle"
        Class="SkiaLabelDefaultStyle"
        TargetType="draw:SkiaLabel">
        <Setter Property="TextColor" Value="#E8E3D7" />
        <Setter Property="FontFamily" Value="FontText" />
        <Setter Property="FontSize" Value="15" />
    </Style>

Apply a style:

<draw:SkiaLabel
    Style="{StaticResource SkiaLabelStyle}"
    Text="Simple Styled Label" />

Apply styles upon conditions:

   <views:SkiaShape
    LockRatio="1"
    Type="Circle"
    WidthRequest="16">
    <views:SkiaControl.Styles>
        <views:ConditionalStyle
            State="Normal"
            Style="{x:StaticResource StyleCameraDot}" />
        <views:ConditionalStyle
            Condition="{Binding .}"
            Style="{x:StaticResource StyleCameraDotOn}" />
    </views:SkiaControl.Styles>

</views:SkiaShape>

Same as:

   <views:SkiaShape
    Style="{StaticResource StyleCameraDot}"
    LockRatio="1"
    Type="Circle"
    WidthRequest="16">
    <views:SkiaControl.Styles>
        <views:ConditionalStyle
            Condition="{Binding .}"
            Style="{x:StaticResource StyleCameraDotOn}" />
    </views:SkiaControl.Styles>

</views:SkiaShape>

When the BindingContext (x:Boolean) is True the style StyleCameraDotOn will be applied, otherwise StyleCameraDot will be applied.

To apply styles upon states you will be using standart Maui VisualStates mechanism, or you can even have serveral states at the same time, every SkiaControl has a string[] States bindable property to server this purpose:

   <views:SkiaShape
    States="{Binding VisualStates}"
	LockRatio="1"
	Type="Circle"
	WidthRequest="16">
	<views:SkiaControl.Styles>
		<views:ConditionalStyle
			State="Normal"
			Style="{x:StaticResource StyleCameraDot}" />
		<views:ConditionalStyle
			State="IsOn"
			Style="{x:StaticResource StyleCameraDotOn}" />
	</views:SkiaControl.Styles>

SkiaShell

The usage is almost the same as the standart Maui Shell, with some extra features.

SkiaShell is derived from FastShell that uses maui interfaces and implements methods for standart maui navigation, then adds features to be able to navigate inside the Canvas.

Some additional features to be mentionned are actions that can be executed for specific routes. code example:

RegisterRoute("profile", typeof(ScreenUserProfile));

RegisterActionRoute("settings", () =>
{
    //select settings tab
    this.NavigationLayout.SelectedIndex = 4;
});

Properties:
  • SkiaLayout RootLayout

  • SkiaViewSwitcher NavigationLayout

  • SkiaControl ShellLayout

Methods:
  • void Initialize(Canvas canvas)

  • GoToAsync(string route, bool animated = true, bool force = false) - Navigates to a route.

  • For navigation on the initialized Canvas you could use

PushDrawnAsync, PopDrawnAsyn c the would push/pop a screen like a usual maui page, but inside the SkiaViewSwitcher.

  • Task (ShellNavigationState page, bool animated = true)
  • Task PushDrawnAsync(BindableObject page, bool animated = true)
Usage

Drawn Controls

SkiaControl

Base drawn element, derived from Maui Element to assure basic Maui Xaml compatibility.

Properties:

LockRatio a numeric value that will be used to calculate the width when the height is set or vice versa. If it's above 0 the max value will be applied, if it's below 0 the min value will be applied. If it's 0 then the ratio will be ignored.

Example 1:

  <draw:SkiaShape
                        LockRatio="1"
                        WidthRequest="40" />

HeightRequest wasn't specified but this control will request 40 by 40 pts.

Example 2:

  <draw:SkiaShape
	LockRatio="-1"
	HeightRequest="30"
	WidthRequest="40" />

This control will request 30 by 30 pts.

SkiaScroll

SkiaScroll is a scrollable container that supports virtualization and recycling of its children.

If you include a SkiaLayout inside a SkiaScroll only visible on screen items will be rendered.

If the include a SkiaLayout that uses ItemTemplate this combination will automatically become virtualized and you will get sort of a CollectionView with recycled cells at your disposal. It is a good practice to use it for long lists of items.

Properties:

Orientation a value of type ScrollOrientation that can be Vertical or Horizontal.

FrictionScrolled Use this to control how fast the scroll will decelerate. Values 0.1f - 0.3f are the best, default is 0.1f.

IgnoreWrongDirection Will ignore gestures of the wrong direction, for example if this Orientation is Horizontal will ignore gestures with vertical direction velocity. Might want to set to true when you have a horizontal scroll inside a vertical scroll, this will let the parent scroll start scrolling vertically ven if gesture started inside its horizontal sroll child.

SkiaLayout

SkiaLayout is a container that supports various layout types: Absolute, Grid, Row, Column and others.

It also supports virtualization and recycling of its children with ItemTemplate property.

Controls inside templated SkiaLayout can implement ISkiaCell interface to eventually receive information about their state:

  • OnAppearing
  • OnDisapearing
  • OnScrolled

This lets one to create custom controls that can react to scrolling and other events with animations etc.

SkiaShape

SkiaShape is a base class for all shapes. You could fill it, stroke, drop shadows, apply gradients and even clip other controls with it.

SkiaImage

SkiaImage is a control that renders images. It cant apply filters and transformations.

SkiaSvg

SkiaSvg is a control that renders svg files. It cant tint the svg with a color or gradient, and apply some transforms to it.

SkiaLabel

A multi-line label fighting for his place under the sun.

Properties:

FontWeight a numeric value used in case you have properly registered your fonts to support weights. You can use your font the usual Maui way but in case of custom font files used from resources you might want to register them, using the following example:

.ConfigureFonts(fonts =>
{
   fonts.AddFont("Gilroy-Regular.ttf", "FontText", FontWeight.Regular);
   fonts.AddFont("Gilroy-Medium.ttf", "FontText", FontWeight.Medium);
});

Now if you set the FontWeight to 500 the control will use the Gilroy-Medium.ttf file. This might come very handy when your Figma design shows you to use this weight and you want just to pass it over to SkiaLabel.

HorizontalTextAlignment :

public enum DrawTextAlignment
{
	Start,
	Center,
	End,
	FillWords,
	FillWordsFull,
	FillCharacters,
	FillCharactersFull,
}

SkiaLottie

SkiaLottie is a control that renders lottie files. It can even tint some colors inside your animation!

SkiaRive

Actually for Windows only, this plays and controls Rive animation files. Other platforms will be added soon, poke if you would like to help biding some c++;

SkiaHoverMask

A control deriving from SkiaShape that can be used to create hover effects. It will render a mask over its children when hovered, think of it as an inverted shape.

SkiaDrawer

A drawer is a container that can be opened and closed, can slide-in and slide-out from the screen. Read more..

SkiaCarousel

To use as slider or carousel. Read more..

Product Compatible and additional computed target framework versions.
.NET net7.0 is compatible.  net7.0-android was computed.  net7.0-android33.0 is compatible.  net7.0-ios was computed.  net7.0-ios16.1 is compatible.  net7.0-maccatalyst was computed.  net7.0-maccatalyst16.1 is compatible.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net7.0-windows10.0.19041 is compatible.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (6)

Showing the top 5 NuGet packages that depend on AppoMobi.Maui.DrawnUi:

Package Downloads
AppoMobi.Maui.Infrastructure

AppoMobi In-house Framework For .NET MAUI

DrawnUi.Maui.Rive

SkiaRive DrawnUi control for .NET MAUI (temporarily Windows only is supported)

DrawnUi.MauiGraphics

SkiaMauiGraphics DrawnUi control for .NET MAUI

DrawnUi.Maui.Camera

Camera implementation (temporarily Android only) and preview rendering with SkiaSharp in .NET MAUI

DrawnUi.Maui.Game

Base class for implementing a game with SkiaSharp in .NET MAUI

GitHub repositories (1)

Showing the top 1 popular GitHub repositories that depend on AppoMobi.Maui.DrawnUi:

Repository Stars
taublast/DrawnUi.Maui
UI Rendering Engine for .NET MAUI powered by SkiaSharp
Version Downloads Last updated
1.3.56.1-pre 45 11/26/2024
1.3.55.1-pre 55 11/25/2024
1.3.54.5-pre 92 11/12/2024
1.3.54.4-pre 43 11/12/2024
1.3.54.2-pre 44 11/9/2024
1.3.54.1-pre 95 11/9/2024
1.3.0.2113-pre 167 4/6/2024
1.3.0.2112-pre 81 4/6/2024 1.3.0.2112-pre is deprecated because it has critical bugs.
1.3.0.219-pre 80 4/3/2024 1.3.0.219-pre is deprecated because it has critical bugs.
1.3.0.218-pre 71 4/3/2024 1.3.0.218-pre is deprecated because it has critical bugs.
1.3.0.23-pre 75 3/26/2024 1.3.0.23-pre is deprecated because it has critical bugs.
1.3.0.22-pre 70 3/26/2024 1.3.0.22-pre is deprecated because it has critical bugs.
1.3.0.21-pre 70 3/26/2024 1.3.0.21-pre is deprecated because it has critical bugs.
1.3.0.2-pre 74 3/26/2024 1.3.0.2-pre is deprecated because it has critical bugs.
1.3.0.1-pre 77 3/26/2024 1.3.0.1-pre is deprecated because it has critical bugs.
1.2.9.7 65 11/26/2024
1.2.9.5 83 11/25/2024
1.2.9.4 79 11/24/2024
1.2.9.2 83 11/21/2024
1.2.9.1 97 11/19/2024
1.2.5.2 118 11/12/2024
1.2.5.1 114 11/5/2024
1.2.4.8 88 11/4/2024 1.2.4.8 is deprecated because it has critical bugs.
1.2.4.7 100 11/2/2024
1.2.4.4 117 10/30/2024
1.2.4.1 103 10/26/2024
1.2.3.8 409 8/21/2024
1.2.3.6 260 7/14/2024
1.2.3.4 216 7/4/2024
1.2.3.3 189 6/27/2024
1.2.3.2 155 6/26/2024
1.2.2.617 156 6/17/2024
1.2.2.67 136 6/7/2024
1.2.2.7 139 6/3/2024
1.2.2.5 192 5/24/2024
1.2.2.4 118 5/24/2024
1.2.2.3 159 5/20/2024
1.2.2.2 109 5/20/2024 1.2.2.2 is deprecated because it has critical bugs.
1.2.2.1 124 5/20/2024 1.2.2.1 is deprecated because it has critical bugs.
1.2.0.95 161 4/14/2024
1.2.0.94 138 4/12/2024
1.2.0.89 248 4/6/2024
1.2.0.88 180 4/6/2024
1.2.0.86 124 4/5/2024
1.2.0.84 132 4/3/2024
1.2.0.83 140 4/3/2024
1.2.0.8 135 4/3/2024
1.2.0.7 161 3/26/2024
1.2.0.6 135 3/26/2024
1.2.0.5 187 3/26/2024
1.2.0.4-pre 135 3/25/2024
1.2.0.3-pre 130 3/25/2024
1.2.0.2-pre 159 3/25/2024
1.0.8.7 216 3/6/2024
1.0.8.6 265 2/27/2024
1.0.8.5 254 2/21/2024
1.0.8.4 251 2/21/2024
1.0.8.3 289 2/16/2024
1.0.8.2 305 2/16/2024
1.0.8.1 323 2/5/2024
1.0.8 307 2/4/2024
1.0.7.6-pre 263 2/1/2024
1.0.7.4-pre 250 2/1/2024
1.0.7.3-pre 270 1/31/2024
1.0.7.2-pre 270 1/31/2024
1.0.7.1-pre 270 1/30/2024
1.0.6.6-pre 266 1/26/2024
1.0.6.5-pre 235 1/26/2024
1.0.6.4-pre 273 1/25/2024
1.0.6.1-pre 259 1/24/2024
1.0.5.8-pre 257 1/22/2024
1.0.5.7-pre 286 1/21/2024
1.0.5.6-pre 275 1/21/2024
1.0.5.5-pre 279 1/21/2024
1.0.5.4-pre 271 1/21/2024
1.0.5.3-pre 273 1/21/2024
1.0.4.6-pre 279 1/20/2024
1.0.3.7-pre 291 1/11/2024 1.0.3.7-pre is deprecated because it has critical bugs.
1.0.3.6-pre 231 1/11/2024
1.0.3.5-pre 282 1/11/2024
1.0.3.4-pre 287 1/10/2024
1.0.3.3-pre 299 1/10/2024
1.0.3.2-pre 269 1/10/2024
1.0.3-pre 274 1/8/2024
1.0.2.9-pre 283 1/7/2024
1.0.2.8-pre 284 1/4/2024
1.0.1.32-pre 314 12/15/2023
1.0.1.31-pre 297 12/15/2023
1.0.1.30-pre 301 12/10/2023
1.0.1.29-pre 279 12/10/2023
1.0.1.28-pre 304 12/8/2023
1.0.1.27-pre 324 11/22/2023
1.0.1.26-pre 289 11/22/2023
1.0.1.25-pre 277 11/21/2023
1.0.1.24-pre 294 11/21/2023 1.0.1.24-pre is deprecated because it has critical bugs.
1.0.1.23-pre 266 11/21/2023
1.0.1.22-pre 298 11/21/2023
1.0.1.21-pre 282 11/21/2023
1.0.1.20-pre 288 11/20/2023
1.0.1.17-pre 282 11/14/2023
1.0.1.15-pre 293 11/9/2023
1.0.1.14-pre 266 11/9/2023
1.0.1.12-pre 295 11/7/2023
1.0.1.11-pre 279 11/2/2023
1.0.1.10-pre 304 11/2/2023
1.0.1.9-pre 312 10/23/2023
1.0.1.8-pre 289 10/23/2023 1.0.1.8-pre is deprecated because it has critical bugs.
1.0.1.6-pre 322 8/3/2023
1.0.1.5-pre 309 8/3/2023

Pre-Alpha