Selaa lähdekoodia

Merge branch 'master' into localization-reference-checker

CPK 3 viikkoa sitten
vanhempi
commit
2a245694cf
100 muutettua tiedostoa jossa 3750 lisäystä ja 492 poistoa
  1. 34 103
      README.md
  2. 1 1
      src/ColorPicker
  3. 1 1
      src/Drawie
  4. 1 1
      src/PixiDocks
  5. 1 1
      src/PixiEditor.AnimationRenderer.FFmpeg/FFMpegRenderer.cs
  6. 2 2
      src/PixiEditor.AnimationRenderer.FFmpeg/PixiEditor.AnimationRenderer.FFmpeg.csproj
  7. 6 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncInputProperty.cs
  8. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Effects/OutlineNode.cs
  9. 6 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/BlurNode.cs
  10. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ColorAdjustmentsFilterNode.cs
  11. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ColorMatrixFilterNode.cs
  12. 4 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/FilterNode.cs
  13. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/GrayscaleNode.cs
  14. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/InvertFilterNode.cs
  15. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/KernelFilterNode.cs
  16. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/SepiaFilterNode.cs
  17. 2 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ShadowNode.cs
  18. 22 4
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LerpColorNode.cs
  19. 18 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Matrix/Matrix3X3BaseNode.cs
  20. 23 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ShaderNode.cs
  21. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsVectorData.cs
  22. 32 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/LineNode.cs
  23. 37 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RectangleNode.cs
  24. 22 8
      src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs
  25. 35 7
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs
  26. 64 6
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/UpdateProperty_Change.cs
  27. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Root/ClipCanvas_Change.cs
  28. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/ApplyMask_Change.cs
  29. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/ImportLayer_Change.cs
  30. 13 0
      src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs
  31. 8 7
      src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs
  32. 27 3
      src/PixiEditor.Desktop/Program.cs
  33. 26 14
      src/PixiEditor.Extensions.Runtime/ExtensionLoader.cs
  34. 2 2
      src/PixiEditor.Extensions.WasmRuntime/WasmMemoryUtility.cs
  35. 2 1
      src/PixiEditor.Extensions/FlyUI/Elements/Text.cs
  36. 1 1
      src/PixiEditor.Extensions/PixiEditor.Extensions.csproj
  37. 1 0
      src/PixiEditor.IdentityProvider.PixiAuth/PixiAuthIdentityProvider.cs
  38. 1 0
      src/PixiEditor.IdentityProvider/IIdentityProvider.cs
  39. 5 0
      src/PixiEditor.Linux/LinuxOperatingSystem.cs
  40. 5 0
      src/PixiEditor.MacOs/MacOperatingSystem.cs
  41. 2 0
      src/PixiEditor.OperatingSystem/IOperatingSystem.cs
  42. 7 7
      src/PixiEditor.PixiAuth/PixiAuthClient.cs
  43. 23 14
      src/PixiEditor.Platform.Standalone/StandaloneAdditionalContentProvider.cs
  44. 2 2
      src/PixiEditor.Platform.Standalone/StandalonePlatform.cs
  45. BIN
      src/PixiEditor.Platform.Steam/OSX-Linux-x64/Steamworks.NET.dll
  46. BIN
      src/PixiEditor.Platform.Steam/OSX-Linux-x64/libsteam_api.so
  47. 1 1
      src/PixiEditor.Platform.Steam/OSX-Linux-x64/steam_api.bundle/Contents/Info.plist
  48. BIN
      src/PixiEditor.Platform.Steam/OSX-Linux-x64/steam_api.bundle/Contents/MacOS/libsteam_api.dylib
  49. 7 10
      src/PixiEditor.Platform.Steam/PixiEditor.Platform.Steam.csproj
  50. 32 4
      src/PixiEditor.Platform.Steam/SteamIdentityProvider.cs
  51. BIN
      src/PixiEditor.Platform.Steam/Windows-x64/Steamworks.NET.dll
  52. BIN
      src/PixiEditor.Platform.Steam/Windows-x64/steam_api64.dll
  53. 4 0
      src/PixiEditor.UI.Common/Accents/Base.axaml
  54. 16 0
      src/PixiEditor.UI.Common/Controls/NumberInput.cs
  55. 9 0
      src/PixiEditor.UI.Common/Localization/Translate.cs
  56. 5 0
      src/PixiEditor.Windows/WindowsOperatingSystem.cs
  57. 8 1
      src/PixiEditor/Data/Localization/Languages/ar.json
  58. 333 14
      src/PixiEditor/Data/Localization/Languages/cs.json
  59. 380 4
      src/PixiEditor/Data/Localization/Languages/de.json
  60. 10 29
      src/PixiEditor/Data/Localization/Languages/en.json
  61. 298 3
      src/PixiEditor/Data/Localization/Languages/pl.json
  62. 8 3
      src/PixiEditor/Data/Localization/Languages/ru.json
  63. 1130 0
      src/PixiEditor/Data/Localization/Languages/tr.json
  64. 297 3
      src/PixiEditor/Data/Localization/Languages/uk.json
  65. 14 7
      src/PixiEditor/Data/Localization/LocalizationData.json
  66. 2 2
      src/PixiEditor/Helpers/Constants/ClipboardDataFormats.cs
  67. 2 1
      src/PixiEditor/Helpers/Converters/FormattedColorConverter.cs
  68. 7 2
      src/PixiEditor/Helpers/Converters/SocketColorConverter.cs
  69. 2 2
      src/PixiEditor/Helpers/Extensions/ColorHelpers.cs
  70. 13 5
      src/PixiEditor/Helpers/Extensions/DataObjectExtensions.cs
  71. 135 1
      src/PixiEditor/Helpers/Extensions/EnumerableExtensions.cs
  72. 15 10
      src/PixiEditor/Helpers/Nodes/NodeAbbreviation.cs
  73. 41 0
      src/PixiEditor/Helpers/RenderApiPreferenceManager.cs
  74. 7 12
      src/PixiEditor/Initialization/ClassicDesktopEntry.cs
  75. 4 1
      src/PixiEditor/Models/AnalyticsAPI/AnalyticsPeriodicReporter.cs
  76. 12 2
      src/PixiEditor/Models/Commands/XAML/NativeMenu.cs
  77. 244 91
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  78. 62 0
      src/PixiEditor/Models/Controllers/IImportObject.cs
  79. 9 0
      src/PixiEditor/Models/DocumentModels/DocumentTransformMode.cs
  80. 4 2
      src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs
  81. 26 3
      src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs
  82. 1 1
      src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs
  83. 4 1
      src/PixiEditor/Models/IO/Paths.cs
  84. 15 3
      src/PixiEditor/Models/Preferences/PreferencesSettings.cs
  85. 18 2
      src/PixiEditor/Models/Rendering/PreviewPainter.cs
  86. 2 2
      src/PixiEditor/PixiEditor.csproj
  87. 2 2
      src/PixiEditor/Properties/AssemblyInfo.cs
  88. 16 6
      src/PixiEditor/RuntimeConstants.cs
  89. 17 35
      src/PixiEditor/Styles/Templates/NodeGraphView.axaml
  90. 15 1
      src/PixiEditor/Styles/Templates/NodePicker.axaml
  91. 1 1
      src/PixiEditor/Styles/Templates/NodeView.axaml
  92. 10 0
      src/PixiEditor/ViewModels/DockableViewModel.cs
  93. 3 2
      src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs
  94. 1 1
      src/PixiEditor/ViewModels/Document/CelGroupViewModel.cs
  95. 5 1
      src/PixiEditor/ViewModels/Document/CelViewModel.cs
  96. 4 1
      src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs
  97. 16 9
      src/PixiEditor/ViewModels/Document/DocumentViewModel.cs
  98. 11 1
      src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs
  99. 9 0
      src/PixiEditor/ViewModels/Document/Nodes/Shapes/LineNodeViewModel.cs
  100. 9 0
      src/PixiEditor/ViewModels/Document/Nodes/Shapes/RectangleNodeViewModel.cs

+ 34 - 103
README.md

@@ -1,8 +1,4 @@
-<img src="https://github.com/user-attachments/assets/bd08c8bd-f610-449d-b1e2-6a990e562518">
-
-
-**PixiEditor** is a universal 2D editor that aims to provide you with tools and features for all your 2D needs. Create beautiful sprites for your games, animations, edit images, create logos. All packed in an eye-friendly dark theme.     
-
+<div align="center">
 
 [![Release](https://img.shields.io/github/v/release/flabbet/PixiEditor)](https://github.com/flabbet/PixiEditor/releases) 
 [![Downloads](https://img.shields.io/github/downloads/PixiEditor/PixiEditor/total)](https://github.com/flabbet/PixiEditor/releases)
@@ -10,130 +6,65 @@
 [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/PixiEditor?label=%20r%2FPixiEditor&logoColor=%23e3002d)](https://reddit.com/r/PixiEditor)
 [![Forum](https://img.shields.io/badge/PixiEditor-Forum-red?link=https%3A%2F%2Fforum.pixieditor.net%2F)](https://forum.pixieditor.net/)
 
-### Check out our website [pixieditor.net](https://pixieditor.net) and [PixiEditor Forum](https://forum.pixieditor.net/)
-
-## About PixiEditor
+<img width="50%" align="center" src="https://github.com/user-attachments/assets/bd08c8bd-f610-449d-b1e2-6a990e562518">
 
-PixiEditor aims to be all-in-one solution for 2D image editing, we want to achieve this by building a solid foundation with built-in tools for editing raster and vector graphics, procedural artworks, animations and more. To fully customize PixiEditor for all of your 2D needs, we built advanced Node Graph rendering, that allows for creating basically anything. From tiled texturing workspace, procedural animations that wouldn't be possible to make by hand, to even rendering 3D shapes.
+## The only 2D Graphics Editor you'll ever need
 
-The project started as a pixel-art editor, but quickly evolved into something much more complex. Version 1.0 was downloaded over 100 000 times on all platforms and received 93% positive rating on Steam.
+**PixiEditor** is a universal 2D editor that was made to provide you with tools and features for all your 2D needs. Create beautiful sprites for your games, animations, edit images, create logos. All packed up in an intuitive and familiar interface.
 
-### Familiar interface
+<a href="https://pixieditor.net/download"><img src="https://github.com/nnakocaj/supreme-train/blob/main/download1.png" width="250" alt="Download"/></a>
 
-Have you ever used Photoshop or Gimp? Reinventing the wheel is unnecessary, we wanted users to get familiar with the tool quickly and with ease. 
+</div>
 
-![](https://opencollective-production.s3.us-west-1.amazonaws.com/account-long-description/d2e269a7-8ded-4e0a-a723-c014730dba1c/PixiEditor_6OoxS5PGVD.png)
+![](https://github.com/nnakocaj/supreme-train/blob/main/interface.png)
 
 ### Toolsets for any scenario
 
-PixiEditor 2.0 comes by default with multiple toolsets: 
-- Pixel art - it contains tool suited for pixel-perfect scenarios
-- Painting - Basic painting tools, soft brushes, anti aliased shapes
-- Vector - Shapes and paths for creating vectors
+PixiEditor 2.0 comes by default with 3 toolsets: 
+- **Pixel art** - it contains tool suited for pixel-perfect scenarios
+- **Painting** - basic painting tools, soft brushes, anti aliased shapes
+- **Vector** - shapes and paths for creating vectors
 
-All toolsets can be used on one canvas, mix vector with raster. Export to png, jpg, svg, gif, mp4 and more!
+All **toolsets can be used on one canvas**. Mix vector with raster. Export to png, jpg, svg, gif, mp4 and more!
 
-![](https://github.com/user-attachments/assets/605c901a-24aa-4c91-9ef9-0fa44878b614)
+<p align="center">
+  <img src="https://github.com/nnakocaj/supreme-train/blob/main/toolsets.gif?raw=true" width="70%" />
+</p>
 
 ### Animations
 
-Version 2.0 comes with Timeline and animation capabilities. You can create frame by frame animations or use nodes to animate your custom shaders.
-Key frame animations with vectors are planned.
+Version 2.0 comes with a timeline and animation capabilities. You can create frame by frame animations or use nodes to animate your custom shaders.
+Key frame animations with vectors are on our roadmap.
 
-![PixiEditor_YdWFRnYxfb](https://github.com/user-attachments/assets/8fba0c6c-35c8-4ccb-9d69-d6beaff5d97f)
+![](https://github.com/nnakocaj/supreme-train/blob/main/timeline1.png)
 
 ### Nodes
 
-Node render system is what powers such extensive capabilities. All layers, effects, layer structure are nodes or a result of node connections. PixiEditor exposes node graph for every document, so you are free to customize your image however you want and create procedural art/animations!
-
-Here are some examples of what you can do with custom nodes https://pixieditor.net/blog/2024/08/16/devlog7#madeinpixieditor20
-
-## Installation - PixiEditor 2.0
-
-Currently version 2.0 is in open beta, follow this guide to install it https://pixieditor.net/docs/open-beta
-
-## Installation PixiEditor 1.0 - Pixel Art Editor
-
-<a href='//www.microsoft.com/store/apps/9NDDRHS8PBRN?cid=storebadge&ocid=badge'><img src='https://developer.microsoft.com/store/badges/images/English_get-it-from-MS.png' alt='Microsoft Store badge' width="184"/></a>
-
-Get it on Steam now!
-
-[![Get PixiEditor on Steam](https://user-images.githubusercontent.com/121322/228988640-32fe5bd3-9dd0-4f3b-a8f2-f744bd9b50b5.png)](https://store.steampowered.com/app/2218560/PixiEditor__Pixel_Art_Editor?utm_source=GitHub)
-
-**Or**
-
-Follow these instructions to get PixiEditor working on your machine.
-
-1. Download the zipped installer from our [official website](https://pixieditor.net/download)
-2. Extract the installer from the archive
-3. Launch it
-4. Follow the steps in the installer to finish the installation
-
-## Featured content
-
-### PixiEditor 1.0 Trailer
-
-[![Trailer](https://img.youtube.com/vi/UK8HnrAQhCo/0.jpg)](https://www.youtube.com/watch?v=UK8HnrAQhCo)
-
-### Pixel Art Timelapse - "Bog Landscape" | PixiEditor
-
-[![Landscape timelapse](https://img.youtube.com/vi/bzC-wy6HCB8/0.jpg)](https://www.youtube.com/watch?v=bzC-wy6HCB8)
-
-### Gallery
-
-Check out some pixel arts made with PixiEditor [here](https://github.com/PixiEditor/PixiEditor/wiki/Gallery).
-
-
-## Support
-
-Struggling with something? You can find support in a few places:
-
-* Check out [documentation](https://pixieditor.net/docs)
-
-* Ask on [Discord](https://discord.gg/qSRMYmq)
-* Check out [Forum](https://forum.pixieditor.net)
-* Open new [Issue](https://github.com/flabbet/PixiEditor/issues)
-* [Get help](https://pixieditor.net/help)
+Node render system is what powers such extensive capabilities. All layers, effects and the layer structure are nodes or a result of its connections. PixiEditor exposes node graph for every document, so you are free to customize your image however you want and create procedural art/animations!
 
+![](https://github.com/nnakocaj/supreme-train/blob/main/node.png)
 
 ## Building from source
 
-### Software Requirements
-
-* .NET 8 SDK
-* [wasi-sdk](https://github.com/WebAssembly/wasi-sdk) - PixiEditor uses WASI modules for extensions
-
-### Instructions
-
-1. Clone Repository with nested submodules
-
-`git clone --recurse-submodules -j8 https://github.com/PixiEditor/PixiEditor.git`
-
-or if cloned already, init submodules with
+<p>Check out our <a href="https://pixieditor.net/docs/contribution/compileguide/">Compile Guide</a></p>
 
-```
-cd PixiEditor
-```
-```
-git submodule update --init --recursive
-```
+## Contributing
 
-2. Download [Wasi-sdk](https://github.com/WebAssembly/wasi-sdk/releases) release for your system
-3. Extract downloaded sdk 
-4. Set `WASI_SDK_PATH` enviroment variable to extracted directory
-5. Run 
-```
-dotnet workload install wasi-experimental
-```
+<p>For a seamless collaboration <a href="https://pixieditor.net/docs/contribution/starthere//">Start Here</a></p>
 
-7. Open PixiEditor/src/PixiEditor.sln in Visual Studio or other IDE of your choice
+## Help
 
-8. Build solution and run PixiEditor.Desktop project
+<p>Got stuck? We are here to <a href="https://pixieditor.net/help">Help</a></p>
 
-## Contributing 
 
-Start with [Contributing Guide](https://github.com/PixiEditor/PixiEditor/blob/master/CONTRIBUTING.md)
+</br></br>
 
-## License
 
-This project is licensed under the LGPLv3 License - see the [LICENSE.md](https://github.com/flabbet/PixiEditor/blob/master/LICENSE) - file for details
+<div align="center">
+    <a href="https://discord.gg/DwaXAuXVzv" target="_blank"><img src="https://newsletter.pixieditor.net/uploads/discord.png" alt="discord" width=50/></a>&nbsp;&nbsp;&nbsp;
+    <a href="https://store.steampowered.com/app/2218560/PixiEditor__Pixel_Art_Editor/" target="_blank"><img src="https://newsletter.pixieditor.net/uploads/steam.png" alt="steam" width=50/></a>&nbsp;&nbsp;&nbsp;
+    <a href="https://www.youtube.com/@PixiEditor" target="_blank"><img src="https://newsletter.pixieditor.net/uploads/youtube.png" alt="youtube" width=50/></a>&nbsp;&nbsp;&nbsp;
+    <a href="https://twitter.com/PixiEditor" target="_blank"><img src="https://newsletter.pixieditor.net/uploads/twitter.png" alt="twitter" width=50/></a>&nbsp;&nbsp;&nbsp;
+    <a href="https://www.reddit.com/r/PixiEditor" target="_blank"><img src="https://newsletter.pixieditor.net/uploads/reddit.png" alt="reddit" width=50/></a>&nbsp;&nbsp;&nbsp;
+    <a href="https://www.linkedin.com/company/pixieditor" target="_blank"><img src="https://newsletter.pixieditor.net/uploads/linkedin.png" alt="linkedin" width=50/></a>
+</div>

+ 1 - 1
src/ColorPicker

@@ -1 +1 @@
-Subproject commit db8aaff273239b21bf4c6f99b0162dcd1d742533
+Subproject commit 943e9abbb60b73c4965b947e987dc2696e0b08f8

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 38fe5456e1e82664ece3eb9f742a29ee60744ed5
+Subproject commit 1be85ac9f4bc6b584e6a3a5a3d0287201c6a5f03

+ 1 - 1
src/PixiDocks

@@ -1 +1 @@
-Subproject commit 642d538aec6799a57e1b170f56f907cab8199fe7
+Subproject commit 6e745d0309ad7a00a53f62f2aa362be77903a5fd

+ 1 - 1
src/PixiEditor.AnimationRenderer.FFmpeg/FFMpegRenderer.cs

@@ -24,7 +24,7 @@ public class FFMpegRenderer : IAnimationRenderer
     {
         string path = $"ThirdParty/{IOperatingSystem.Current.Name}/ffmpeg";
 
-        string binaryPath = Path.Combine(Path.GetDirectoryName(Environment.ProcessPath), path);
+        string binaryPath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), path);
 
         GlobalFFOptions.Configure(new FFOptions() { BinaryFolder = binaryPath });
 

+ 2 - 2
src/PixiEditor.AnimationRenderer.FFmpeg/PixiEditor.AnimationRenderer.FFmpeg.csproj

@@ -32,13 +32,13 @@
   </ItemGroup>
 
   <ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-x64'">
-    <Content Include="ThirdParty\MacOS\ffmpeg\**">
+    <Content Include="ThirdParty/MacOS/ffmpeg/**">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
 
   <ItemGroup Condition="'$(RuntimeIdentifier)' == 'osx-arm64'">
-    <Content Include="ThirdParty\MacOS\ffmpeg\**">
+    <Content Include="ThirdParty/MacOS/ffmpeg/**">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>

+ 6 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncInputProperty.cs

@@ -65,10 +65,15 @@ public class FuncInputProperty<T> : InputProperty<Func<FuncContext, T>>, IFuncIn
                 else if (sourceObj is Expression expression)
                 {
                     ShaderExpressionVariable shaderExpressionVariable = (ShaderExpressionVariable)toReturn;
-                    shaderExpressionVariable.OverrideExpression = Adjust(expression, toReturn, out var adjustNested);
+                    var adjusted = Adjust(expression, toReturn, out var adjustNested);
                     if (adjustNested)
                     {
                         AdjustNested(((IMultiValueVariable)toReturn), expression);
+                        shaderExpressionVariable.OverrideExpression = ((IMultiValueVariable)toReturn).GetWholeNestedExpression();
+                    }
+                    else
+                    {
+                        shaderExpressionVariable.OverrideExpression = adjusted;
                     }
                 }
 
@@ -91,7 +96,6 @@ public class FuncInputProperty<T> : InputProperty<Func<FuncContext, T>>, IFuncIn
         if (toReturn is IMultiValueVariable)
         {
             adjustNestedVariables = true;
-            return expression;
         }
 
         return expression;

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Effects/OutlineNode.cs

@@ -114,7 +114,7 @@ public class OutlineNode : RenderNode, IRenderInput
             surface.Canvas.RestoreToCount(saved);
         }
 
-        Background.Value.Paint(context, surface);
+        Background?.Value?.Paint(context, surface);
     }
 
     public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")

+ 6 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/BlurNode.cs

@@ -2,6 +2,7 @@
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -11,16 +12,18 @@ public class BlurNode : FilterNode
     public InputProperty<bool> PreserveAlpha { get; }
     
     public InputProperty<VecD> Radius { get; }
-    
+
+    protected override bool ExecuteOnlyOnCacheChange => true;
+
     public BlurNode()
     {
         PreserveAlpha = CreateInput("PreserveAlpha", "PRESERVE_ALPHA", true);
         Radius = CreateInput("Radius", "RADIUS", new VecD(1, 1)).WithRules(x => x.Min(new VecD(0, 0)));
     }
 
-    protected override ImageFilter GetImageFilter()
+    protected override ImageFilter? GetImageFilter(RenderContext context)
     {
-        var sigma = (VecF)Radius.Value;
+        var sigma = (VecF)(Radius.Value * context.ChunkResolution.Multiplier());
         var preserveAlpha = PreserveAlpha.Value;
 
         var xFilter = GetGaussianFilter(sigma.X, true, preserveAlpha, null, out float[] xKernel);

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ColorAdjustmentsFilterNode.cs

@@ -1,5 +1,6 @@
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -54,7 +55,7 @@ public class ColorAdjustmentsFilterNode : FilterNode
             .WithRules(rules => rules.Min(-180d).Max(180d));
     }
 
-    protected override ColorFilter? GetColorFilter()
+    protected override ColorFilter? GetColorFilter(RenderContext context)
     {
         filters.ForEach(filter => filter.Dispose());
         filters.Clear();

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ColorMatrixFilterNode.cs

@@ -1,6 +1,7 @@
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -17,7 +18,7 @@ public class ColorMatrixFilterNode : FilterNode
         Matrix = CreateInput(nameof(Matrix), "MATRIX", ColorMatrix.Identity);
     }
 
-    protected override ColorFilter? GetColorFilter()
+    protected override ColorFilter? GetColorFilter(RenderContext context)
     {
         if (Matrix.Value.Equals(lastMatrix))
         {

+ 4 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/FilterNode.cs

@@ -20,8 +20,8 @@ public abstract class FilterNode : Node
 
     protected override void OnExecute(RenderContext context)
     {
-        var colorFilter = GetColorFilter();
-        var imageFilter = GetImageFilter();
+        var colorFilter = GetColorFilter(context);
+        var imageFilter = GetImageFilter(context);
 
         if (colorFilter == null && imageFilter == null)
         {
@@ -34,7 +34,7 @@ public abstract class FilterNode : Node
         Output.Value = filter == null ? new Filter(colorFilter, imageFilter) : filter.Add(colorFilter, imageFilter);
     }
 
-    protected virtual ColorFilter? GetColorFilter() => null;
+    protected virtual ColorFilter? GetColorFilter(RenderContext context) => null;
 
-    protected virtual ImageFilter? GetImageFilter() => null;
+    protected virtual ImageFilter? GetImageFilter(RenderContext context) => null;
 }

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/GrayscaleNode.cs

@@ -1,6 +1,7 @@
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -34,7 +35,7 @@ public class GrayscaleNode : FilterNode
         CustomWeight = CreateInput("CustomWeight", "WEIGHT_FACTOR", new Vec3D(1, 1, 1));
     }
 
-    protected override ColorFilter? GetColorFilter()
+    protected override ColorFilter? GetColorFilter(RenderContext context)
     {
         if (Mode.Value == lastMode 
             && Factor.Value == lastFactor 

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/InvertFilterNode.cs

@@ -1,6 +1,7 @@
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -20,7 +21,7 @@ public class InvertFilterNode : FilterNode
         filter = ColorFilter.CreateColorMatrix(invertedMatrix);
     }
 
-    protected override ColorFilter? GetColorFilter()
+    protected override ColorFilter? GetColorFilter(RenderContext context)
     {
         filter?.Dispose();
 

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/KernelFilterNode.cs

@@ -1,6 +1,7 @@
 using Drawie.Backend.Core.Surfaces;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -37,7 +38,7 @@ public class KernelFilterNode : FilterNode
         OnAlpha = CreateInput(nameof(OnAlpha), "ON_ALPHA", false);
     }
 
-    protected override ImageFilter? GetImageFilter()
+    protected override ImageFilter? GetImageFilter(RenderContext context)
     {
         var kernel = Kernel.Value;
         

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/SepiaFilterNode.cs

@@ -2,6 +2,7 @@
 using Drawie.Backend.Core.Surfaces.ImageData;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -32,7 +33,7 @@ public class SepiaFilterNode : FilterNode
         );
     }
 
-    protected override ColorFilter? GetColorFilter()
+    protected override ColorFilter? GetColorFilter(RenderContext context)
     {
         lastFilter?.Dispose();
 

+ 2 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FilterNodes/ShadowNode.cs

@@ -1,6 +1,7 @@
 using Drawie.Backend.Core.ColorsImpl;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
 
@@ -18,7 +19,7 @@ public class ShadowNode : FilterNode
         Color = CreateInput("Color", "COLOR", Colors.Black);
     }
 
-    protected override ImageFilter? GetImageFilter()
+    protected override ImageFilter? GetImageFilter(RenderContext context)
     {
         return ImageFilter.CreateDropShadow((float)Offset.Value.X, (float)Offset.Value.Y, (float)Sigma.Value.X, (float)Sigma.Value.Y, Color.Value, null);
     }

+ 22 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/LerpColorNode.cs

@@ -10,11 +10,11 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 [NodeInfo("Lerp")]
 public class LerpColorNode : Node // TODO: ILerpable as inputs? 
 {
-    public FuncOutputProperty<Half4> Result { get; } 
+    public FuncOutputProperty<Half4> Result { get; }
     public FuncInputProperty<Half4> From { get; }
     public FuncInputProperty<Half4> To { get; }
     public FuncInputProperty<Float1> Time { get; }
-    
+
     public LerpColorNode()
     {
         Result = CreateFuncOutput<Half4>("Result", "RESULT", Lerp);
@@ -28,8 +28,26 @@ public class LerpColorNode : Node // TODO: ILerpable as inputs?
         var from = arg.GetValue(From);
         var to = arg.GetValue(To);
         var time = arg.GetValue(Time);
-        
-        return arg.NewHalf4(ShaderMath.Lerp(from, to, time)); 
+
+        if (arg.HasContext)
+        {
+            return arg.NewHalf4(ShaderMath.Lerp(from, to, time));
+        }
+
+        var constFrom = (Color)from.GetConstant();
+        var constTo = (Color)to.GetConstant();
+        var constTime = time.GetConstant();
+
+        double dTime = constTime switch
+        {
+            double d => d,
+            int i => i,
+            float f => f,
+            _ => throw new InvalidOperationException("Unsupported constant type for time.")
+        };
+
+        Color result = Color.Lerp(constFrom, constTo, dTime);
+        return new Half4("") { ConstantValue = result };
     }
 
     protected override void OnExecute(RenderContext context)

+ 18 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Matrix/Matrix3X3BaseNode.cs

@@ -40,7 +40,8 @@ public abstract class Matrix3X3BaseNode : RenderNode, IRenderInput
 
         Float3x3 mtx = Matrix.Value.Invoke(FuncContext.NoContext);
 
-        surface.Canvas.SetMatrix(surface.Canvas.TotalMatrix.Concat(mtx.GetConstant() as Matrix3X3? ?? Matrix3X3.Identity));
+        surface.Canvas.SetMatrix(
+            surface.Canvas.TotalMatrix.Concat(mtx.GetConstant() as Matrix3X3? ?? Matrix3X3.Identity));
         if (!surface.LocalClipBounds.IsZeroOrNegativeArea)
         {
             Background.Value?.Paint(context, surface);
@@ -49,5 +50,21 @@ public abstract class Matrix3X3BaseNode : RenderNode, IRenderInput
         surface.Canvas.RestoreToCount(layer);
     }
 
+    public override RectD? GetPreviewBounds(int frame, string elementToRenderName = "")
+    {
+        if (Background.Value == null)
+            return null;
+
+        return base.GetPreviewBounds(frame, elementToRenderName);
+    }
+
+    public override bool RenderPreview(DrawingSurface renderOn, RenderContext context, string elementToRenderName)
+    {
+        if (Background.Value == null)
+            return false;
+
+        return base.RenderPreview(renderOn, context, elementToRenderName);
+    }
+
     protected abstract Float3x3 CalculateMatrix(FuncContext ctx, Float3x3 input);
 }

+ 23 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ShaderNode.cs

@@ -32,11 +32,26 @@ public class ShaderNode : RenderNode, IRenderInput, ICustomShaderNode
     protected override bool ExecuteOnlyOnCacheChange => true;
     protected override CacheTriggerFlags CacheTrigger => CacheTriggerFlags.All;
 
+    private string defaultShaderCode = """
+                                       // Below is a list of built-in special uniforms that are automatically added by PixiEditor.
+                                       // Any other uniform will be added as a Node input
+                                       
+                                       uniform vec2 iResolution; // The resolution of current render output. It is usually a document size.
+                                       uniform float iNormalizedTime; // The normalized time of the current frame, from 0 to 1.
+                                       uniform int iFrame; // The current frame number.
+                                       uniform shader iImage; // The Background input of the node, alternatively you can use "Background" uniform.
+                                       
+                                       half4 main(float2 uv)
+                                       {
+                                           return half4(1, 1, 1, 1);
+                                       }
+                                       """;
+
     public ShaderNode()
     {
         Background = CreateRenderInput("Background", "BACKGROUND");
         ColorSpace = CreateInput("ColorSpace", "COLOR_SPACE", ColorSpaceType.Inherit);
-        ShaderCode = CreateInput("ShaderCode", "SHADER_CODE", "")
+        ShaderCode = CreateInput("ShaderCode", "SHADER_CODE", defaultShaderCode)
             .WithRules(validator => validator.Custom(ValidateShaderCode))
             .NonOverridenChanged(RegenerateUniformInputs);
 
@@ -118,6 +133,7 @@ public class ShaderNode : RenderNode, IRenderInput, ICustomShaderNode
         lastImageShader = snapshot.ToShader();
 
         uniforms.Add("iImage", new Uniform("iImage", lastImageShader));
+        uniforms.Add("Background", new Uniform("Background", lastImageShader));
 
         snapshot.Dispose();
         //texture.Dispose();
@@ -220,6 +236,11 @@ public class ShaderNode : RenderNode, IRenderInput, ICustomShaderNode
         var uniforms = declarations;
 
         var nonExistingUniforms = uniformInputs.Keys.Where(x => uniforms.All(y => y.Name != x)).ToList();
+        if(nonExistingUniforms.Contains("Background"))
+        {
+            nonExistingUniforms.Remove("Background");
+        }
+
         foreach (var nonExistingUniform in nonExistingUniforms)
         {
             RemoveInputProperty(uniformInputs[nonExistingUniform].prop);
@@ -388,7 +409,7 @@ public class ShaderNode : RenderNode, IRenderInput, ICustomShaderNode
 
     private bool IsBuiltInUniform(string name)
     {
-        return name is "iResolution" or "iNormalizedTime" or "iFrame" or "iImage";
+        return name is "iResolution" or "iNormalizedTime" or "iFrame" or "iImage" or "Background";
     }
 
     private ValidatorResult ValidateShaderCode(object? value)

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsVectorData.cs

@@ -16,7 +16,7 @@ public class PointsVectorData : ShapeVectorData
         Points = new List<VecD>(points);
     }
 
-    public override RectD GeometryAABB => new RectD(Points.Min(p => p.X), Points.Min(p => p.Y), Points.Max(p => p.X),
+    public override RectD GeometryAABB => Points == null || Points.Count == 0 ? RectD.Empty : new RectD(Points.Min(p => p.X), Points.Min(p => p.Y), Points.Max(p => p.X),
         Points.Max(p => p.Y));
 
     public override RectD VisualAABB => GeometryAABB;

+ 32 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/LineNode.cs

@@ -0,0 +1,32 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl.Paintables;
+using Drawie.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+
+[NodeInfo("Line")]
+public class LineNode : ShapeNode<LineVectorData>
+{
+    public InputProperty<VecD> Start { get; }
+    public InputProperty<VecD> End { get; }
+    public InputProperty<Paintable> StrokeColor { get; }
+    public InputProperty<double> StrokeWidth { get; }
+
+    public LineNode()
+    {
+        Start = CreateInput<VecD>("LineStart", "LINE_START", VecD.Zero);
+        End = CreateInput<VecD>("LineEnd", "LINE_END", new VecD(32, 32));
+        StrokeColor = CreateInput<Paintable>("StrokeColor", "STROKE_COLOR", new Color(0, 0, 0, 255));
+        StrokeWidth = CreateInput<double>("StrokeWidth", "STROKE_WIDTH", 1);
+    }
+
+    protected override LineVectorData? GetShapeData(RenderContext context)
+    {
+        return new LineVectorData(Start.Value, End.Value)
+            { Stroke = StrokeColor.Value, StrokeWidth = (float)StrokeWidth.Value };
+    }
+
+    public override Node CreateCopy() => new LineNode();
+}

+ 37 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RectangleNode.cs

@@ -0,0 +1,37 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl.Paintables;
+using Drawie.Numerics;
+
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+
+[NodeInfo("Rectangle")]
+public class RectangleNode : ShapeNode<RectangleVectorData>
+{
+    public InputProperty<VecD> Center { get; }
+    public InputProperty<VecD> Size { get; }
+    public InputProperty<double> CornerRadius { get; }
+    public InputProperty<Paintable> StrokeColor { get; }
+    public InputProperty<Paintable> FillColor { get; }
+    public InputProperty<double> StrokeWidth { get; }
+
+    public RectangleNode()
+    {
+        Center = CreateInput<VecD>("Position", "POSITION", VecI.Zero);
+        Size = CreateInput<VecD>("Size", "SIZE", new VecD(32, 32)).WithRules(
+            v => v.Min(new VecD(0)));
+        CornerRadius = CreateInput<double>("CornerRadius", "RADIUS", 0);
+        StrokeColor = CreateInput<Paintable>("StrokeColor", "STROKE_COLOR", new Color(0, 0, 0, 255));
+        FillColor = CreateInput<Paintable>("FillColor", "FILL_COLOR", new Color(0, 0, 0, 255));
+        StrokeWidth = CreateInput<double>("StrokeWidth", "STROKE_WIDTH", 1);
+    }
+
+    protected override RectangleVectorData? GetShapeData(RenderContext context)
+    {
+        return new RectangleVectorData(Center.Value, Size.Value)
+            { CornerRadius = CornerRadius.Value, Stroke = StrokeColor.Value, FillPaintable = FillColor.Value, StrokeWidth = (float)StrokeWidth.Value };
+    }
+
+    public override Node CreateCopy() => new RectangleNode();
+}

+ 22 - 8
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -4,6 +4,8 @@ using PixiEditor.ChangeableDocument.Changes.Structure;
 using PixiEditor.ChangeableDocument.Rendering;
 using Drawie.Backend.Core;
 using Drawie.Backend.Core.Bridge;
+using Drawie.Backend.Core.ColorsImpl;
+using Drawie.Backend.Core.ColorsImpl.Paintables;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
@@ -144,6 +146,11 @@ internal class CombineStructureMembersOnto_Change : Change
 
         var ordererd = OrderLayers(layersToCombine, target);
 
+        if (ordererd.Count == 0)
+        {
+            return changes;
+        }
+
         foreach (var guid in ordererd)
         {
             var layer = target.FindMemberOrThrow<StructureNode>(guid);
@@ -215,6 +222,11 @@ internal class CombineStructureMembersOnto_Change : Change
             }
             else
             {
+                if (targetPath == null)
+                {
+                    targetPath = new VectorPath();
+                }
+
                 targetPath.AddPath(path, vectorNode.EmbeddedShapeData.TransformationMatrix, AddPathMode.Append);
                 path.Dispose();
             }
@@ -227,10 +239,10 @@ internal class CombineStructureMembersOnto_Change : Change
             ShapeVectorData shape = clone as ShapeVectorData;
             data = new PathVectorData(targetPath)
             {
-                Stroke = shape.Stroke,
-                FillPaintable = shape.FillPaintable,
-                StrokeWidth = shape.StrokeWidth,
-                Fill = shape.Fill,
+                Stroke = shape?.Stroke,
+                FillPaintable = shape?.FillPaintable,
+                StrokeWidth = shape?.StrokeWidth ?? 1,
+                Fill = shape?.Fill ?? true,
                 TransformationMatrix = Matrix3X3.Identity
             };
         }
@@ -248,9 +260,9 @@ internal class CombineStructureMembersOnto_Change : Change
 
     private AffectedArea RasterMerge(Document target, StructureNode targetLayer, int frame)
     {
-        if(targetLayer is not ImageLayerNode)
+        if (targetLayer is not ImageLayerNode)
             throw new InvalidOperationException("Target layer is not a raster layer");
-        
+
         var toDrawOnImage = ((ImageLayerNode)targetLayer).GetLayerImageAtFrame(frame);
         toDrawOnImage.EnqueueClear();
 
@@ -261,7 +273,8 @@ internal class CombineStructureMembersOnto_Change : Change
         AffectedArea affArea = new();
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
-            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full, target.Size);
+            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full,
+                target.Size);
 
             toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
 
@@ -288,7 +301,8 @@ internal class CombineStructureMembersOnto_Change : Change
         return ordered.Reverse().ToHashSet();
     }
 
-    private void AddMissingKeyFrame(StructureNode targetLayer, int frame, StructureNode layer, List<IChangeInfo> changes,
+    private void AddMissingKeyFrame(StructureNode targetLayer, int frame, StructureNode layer,
+        List<IChangeInfo> changes,
         Document target)
     {
         bool hasKeyframe = targetLayer.KeyFrames.Any(x => x.IsInFrame(frame));

+ 35 - 7
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs

@@ -72,9 +72,11 @@ public static class NodeOperations
         return node;
     }
 
-    public static List<IChangeInfo> AppendMember(Node parent, Node toAppend, out Dictionary<Guid, VecD> originalPositions)
+    public static List<IChangeInfo> AppendMember(Node parent, Node toAppend,
+        out Dictionary<Guid, VecD> originalPositions)
     {
-        InputProperty<Painter?>? parentInput = parent.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter?>;
+        InputProperty<Painter?>? parentInput =
+            parent.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter?>;
         if (parentInput == null)
         {
             throw new InvalidOperationException("Parent node does not have an input property for appending members.");
@@ -86,18 +88,21 @@ public static class NodeOperations
             throw new InvalidOperationException("Node to append does not have an output property named 'Output'.");
         }
 
-        InputProperty<Painter>? toAddInput = toAppend.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter>;
+        InputProperty<Painter>? toAddInput =
+            toAppend.GetInputProperty(OutputNode.InputPropertyName) as InputProperty<Painter>;
 
         if (toAddInput == null)
         {
-            throw new InvalidOperationException("Node to append does not have an input property for appending members.");
+            throw new InvalidOperationException(
+                "Node to append does not have an input property for appending members.");
         }
 
         Guid memberId = toAppend.Id;
 
         List<IChangeInfo> changes = AppendMember(parentInput, toAddOutput, toAddInput, memberId);
 
-        var adjustedPositions = AdjustPositionsAfterAppend(toAppend, parent, parentInput.Connection?.Node as Node ?? null, out originalPositions);
+        var adjustedPositions = AdjustPositionsAfterAppend(toAppend, parent,
+            parentInput.Connection?.Node as Node ?? null, out originalPositions);
 
         changes.AddRange(adjustedPositions);
         return changes;
@@ -110,6 +115,9 @@ public static class NodeOperations
     {
         List<IChangeInfo> changes = new();
         IOutputProperty? previouslyConnected = null;
+
+        if(parentInput == null) return changes;
+
         if (parentInput.Connection != null)
         {
             previouslyConnected = parentInput.Connection;
@@ -215,8 +223,8 @@ public static class NodeOperations
                 toMove.Position = pos;
                 toMove.Position = new VecD(toMove.Position.X, y);
                 changes.Add(new NodePosition_ChangeInfo(toMove.Id, toMove.Position));
-                
-                if(aNode == appendedTo) return false;
+
+                if (aNode == appendedTo) return false;
             }
 
             return true;
@@ -270,15 +278,35 @@ public static class NodeOperations
     public static List<IChangeInfo> ConnectStructureNodeProperties(ConnectionsData originalConnections, Node node,
         IReadOnlyNodeGraph graph)
     {
+        if (node == null || originalConnections == null || graph == null)
+        {
+            return new List<IChangeInfo>();
+        }
+
         List<IChangeInfo> changes = new();
         foreach (var connections in originalConnections.originalOutputConnections)
         {
             PropertyConnection outputConnection = connections.Key;
+            if (outputConnection == null)
+                continue;
+
             IOutputProperty outputProp = node.GetOutputProperty(outputConnection.PropertyName);
+
+            if (outputProp == null)
+            {
+                continue;
+            }
+
             foreach (var connection in connections.Value)
             {
                 var inputNode = graph.AllNodes.FirstOrDefault(x => x.Id == connection.NodeId);
+                if (inputNode is null)
+                    continue;
+
                 IInputProperty property = inputNode.GetInputProperty(connection.PropertyName);
+                if (property is null)
+                    continue;
+
                 outputProp.ConnectTo(property);
                 changes.Add(new ConnectProperty_ChangeInfo(node.Id, property.Node.Id, outputProp.InternalPropertyName,
                     property.InternalPropertyName));

+ 64 - 6
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/UpdateProperty_Change.cs

@@ -1,17 +1,18 @@
-using PixiEditor.ChangeableDocument.Changeables.Graph;
+using Drawie.Backend.Core.Shaders.Generation;
+using PixiEditor.ChangeableDocument.Changeables.Graph;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.ChangeableDocument.ChangeInfos.NodeGraph;
 
 namespace PixiEditor.ChangeableDocument.Changes.NodeGraph;
 
-internal class UpdatePropertyValue_Change : Change
+internal class UpdatePropertyValue_Change : InterruptableUpdateableChange
 {
     private readonly Guid _nodeId;
     private readonly string _propertyName;
     private object? _value;
     private object? previousValue;
 
-    [GenerateMakeChangeAction]
+    [GenerateUpdateableChangeActions]
     public UpdatePropertyValue_Change(Guid nodeId, string property, object? value)
     {
         _nodeId = nodeId;
@@ -23,12 +24,69 @@ internal class UpdatePropertyValue_Change : Change
     {
         if (target.TryFindNode<Node>(_nodeId, out var node))
         {
-            return node.HasInputProperty(_propertyName);
+            var property = node.GetInputProperty(_propertyName);
+            if (property == null) return false;
+
+            previousValue = GetValue(property);
+            if (previousValue is ShaderExpressionVariable expr)
+            {
+                previousValue = expr.GetConstant();
+            }
+
+            return true;
         }
 
         return false;
     }
 
+    [UpdateChangeMethod]
+    public void UpdateValue(object? value)
+    {
+        _value = value;
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
+    {
+        var node = target.NodeGraph.Nodes.First(x => x.Id == _nodeId);
+        var property = node.GetInputProperty(_propertyName);
+
+        int inputsHash = CalculateInputsHash(node);
+        int outputsHash = CalculateOutputsHash(node);
+
+        string errors = string.Empty;
+        if (!property.Validator.Validate(_value, out errors))
+        {
+            if (string.IsNullOrEmpty(errors))
+            {
+                _value = property.Validator.GetClosestValidValue(_value);
+            }
+
+            _value = SetValue(property, _value);
+        }
+        else
+        {
+            _value = SetValue(property, _value);
+        }
+
+        List<IChangeInfo> changes = new();
+        changes.Add(new PropertyValueUpdated_ChangeInfo(_nodeId, _propertyName, _value) { Errors = errors });
+
+        int newInputsHash = CalculateInputsHash(node);
+        int newOutputsHash = CalculateOutputsHash(node);
+
+        if (inputsHash != newInputsHash)
+        {
+            changes.Add(NodeInputsChanged_ChangeInfo.FromNode(node));
+        }
+
+        if (outputsHash != newOutputsHash)
+        {
+            changes.Add(NodeOutputsChanged_ChangeInfo.FromNode(node));
+        }
+
+        return changes;
+    }
+
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
         out bool ignoreInUndo)
     {
@@ -38,7 +96,6 @@ internal class UpdatePropertyValue_Change : Change
         int inputsHash = CalculateInputsHash(node);
         int outputsHash = CalculateOutputsHash(node);
 
-        previousValue = GetValue(property);
         string errors = string.Empty;
         if (!property.Validator.Validate(_value, out errors))
         {
@@ -165,6 +222,7 @@ internal class UpdatePropertyValue_Change : Change
 
     public override bool IsMergeableWith(Change other)
     {
-        return other is UpdatePropertyValue_Change change && change._nodeId == _nodeId && change._propertyName == _propertyName;
+        return other is UpdatePropertyValue_Change change && change._nodeId == _nodeId &&
+               change._propertyName == _propertyName && _value == change._value;
     }
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changes/Root/ClipCanvas_Change.cs

@@ -45,8 +45,8 @@ internal class ClipCanvas_Change : ResizeBasedChangeBase
         VecI size = (VecI)newBounds.Size.Ceiling();
         
         target.Size = size;
-        target.VerticalSymmetryAxisX = Math.Clamp(_originalVerAxisX, 0, target.Size.X);
-        target.HorizontalSymmetryAxisY = Math.Clamp(_originalHorAxisY, 0, target.Size.Y);
+        target.VerticalSymmetryAxisX = Math.Clamp(_originalVerAxisX, 0, Math.Max(target.Size.X, 1));
+        target.HorizontalSymmetryAxisY = Math.Clamp(_originalHorAxisY, 0, Math.Max(target.Size.Y, 1));
         
         target.ForEveryMember((member) =>
         {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Structure/ApplyMask_Change.cs

@@ -21,7 +21,7 @@ internal sealed class ApplyMask_Change : Change
     public override bool InitializeAndValidate(Document target)
     {
         var member = target.FindMember(structureMemberGuid);
-        bool isValid = member is not (null or FolderNode) && member.EmbeddedMask is not null;
+        bool isValid = member is ImageLayerNode && member.EmbeddedMask is not null;
 
         return isValid;
     }

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Structure/ImportLayer_Change.cs

@@ -61,7 +61,7 @@ internal class ImportLayer_Change : Change
 
         var clone = (LayerNode)layerNode.Clone();
         clone.Id = duplicateGuid;
-        clonedLayer = clone;
+        clonedLayer ??= clone.Clone(true) as LayerNode;
 
         ResizeImageData(clone, target.Size);
 

+ 13 - 0
src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs

@@ -38,6 +38,19 @@ public class DocumentChangeTracker : IDisposable
     {
         if (running)
             throw new InvalidOperationException("Something is currently being processed");
+
+        if (activeUpdateableChange != null)
+        {
+            try
+            {
+                activeUpdateableChange.Apply(document, false, out var _);
+            }
+            catch (Exception e)
+            {
+                Trace.WriteLine($"Failed to apply active updateable change {activeUpdateableChange}: {e}");
+            }
+        }
+
         if (disposed)
             return;
         disposed = true;

+ 8 - 7
src/PixiEditor.ChangeableDocument/Rendering/DocumentRenderer.cs

@@ -125,18 +125,18 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
         IsBusy = false;
     }
 
-    public async Task RenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn,
+    public async Task<bool> RenderNodePreview(IPreviewRenderable previewRenderable, DrawingSurface renderOn,
         RenderContext context,
         string elementToRenderName)
     {
-        if (previewRenderable is Node { IsDisposed: true }) return;
+        if (previewRenderable is Node { IsDisposed: true }) return false;
         TaskCompletionSource<bool> tcs = new();
         RenderRequest request = new(tcs, context, renderOn, previewRenderable, elementToRenderName);
 
         renderRequests.Enqueue(request);
         ExecuteRenderRequests();
 
-        await tcs.Task;
+        return await tcs.Task;
     }
 
     public static IReadOnlyNodeGraph ConstructMembersOnlyGraph(IReadOnlyNodeGraph fullGraph)
@@ -212,7 +212,7 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
     public void RenderDocument(DrawingSurface toRenderOn, KeyFrameTime frameTime, VecI renderSize,
         string? customOutput = null)
     {
-        var ctx = DrawingBackendApi.Current.RenderingDispatcher.EnsureContext();
+        using var ctx = DrawingBackendApi.Current.RenderingDispatcher.EnsureContext();
         IsBusy = true;
 
         if (renderTexture == null || renderTexture.Size != renderSize)
@@ -264,7 +264,6 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
         renderTexture.DrawingSurface.Canvas.Restore();
         toRenderOn.Canvas.Restore();
 
-        ctx.Dispose();
         IsBusy = false;
     }
 
@@ -273,15 +272,17 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
         if (isExecuting) return;
 
         isExecuting = true;
+        using var ctx = DrawingBackendApi.Current?.RenderingDispatcher.EnsureContext();
         while (renderRequests.Count > 0)
         {
             RenderRequest request = renderRequests.Dequeue();
 
             try
             {
+                bool result = true;
                 if (request.PreviewRenderable != null)
                 {
-                    request.PreviewRenderable.RenderPreview(request.RenderOn, request.Context,
+                    result = request.PreviewRenderable.RenderPreview(request.RenderOn, request.Context,
                         request.ElementToRenderName);
                 }
                 else if (request.NodeGraph != null)
@@ -289,7 +290,7 @@ public class DocumentRenderer : IPreviewRenderable, IDisposable
                     request.NodeGraph.Execute(request.Context);
                 }
 
-                request.TaskCompletionSource.SetResult(true);
+                request.TaskCompletionSource.SetResult(result);
             }
             catch (Exception e)
             {

+ 27 - 3
src/PixiEditor.Desktop/Program.cs

@@ -1,8 +1,10 @@
 using System;
+using System.Linq;
 using Avalonia;
 using Avalonia.Logging;
 using Drawie.Interop.Avalonia;
 using Drawie.Interop.VulkanAvalonia;
+using PixiEditor.Helpers;
 
 namespace PixiEditor.Desktop;
 
@@ -17,16 +19,37 @@ public class Program
 
     // Avalonia configuration, don't remove; also used by visual designer.
     public static AppBuilder BuildAvaloniaApp()
-        => AppBuilder.Configure<App>()
+    {
+        bool openGlPreferred = false;
+        try
+        {
+            openGlPreferred = string.Equals(RenderApiPreferenceManager.TryReadRenderApiPreference(), "opengl",
+                StringComparison.OrdinalIgnoreCase);
+
+            if (!openGlPreferred)
+            {
+                var cmdArgs = Environment.GetCommandLineArgs();
+                if (cmdArgs is { Length: > 0 })
+                {
+                    openGlPreferred = cmdArgs.Any(arg =>
+                        string.Equals(arg, "--opengl", StringComparison.OrdinalIgnoreCase));
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+        }
+
+        return AppBuilder.Configure<App>()
             .UsePlatformDetect()
             .With(new Win32PlatformOptions()
             {
-                RenderingMode = new Win32RenderingMode[] { Win32RenderingMode.Vulkan },
+                RenderingMode = openGlPreferred ? [ Win32RenderingMode.Wgl, Win32RenderingMode.Vulkan] : [ Win32RenderingMode.Vulkan, Win32RenderingMode.Wgl],
                 OverlayPopups = true,
             })
             .With(new X11PlatformOptions()
             {
-                RenderingMode = new X11RenderingMode[] { X11RenderingMode.Vulkan },
+                RenderingMode = openGlPreferred ? [ X11RenderingMode.Glx, X11RenderingMode.Vulkan] : [ X11RenderingMode.Vulkan, X11RenderingMode.Glx],
                 OverlayPopups = true,
             })
             .With(new SkiaOptions()
@@ -38,4 +61,5 @@ public class Program
             .LogToTrace(LogEventLevel.Verbose, "Vulkan")
 #endif
             .LogToTrace();
+    }
 }

+ 26 - 14
src/PixiEditor.Extensions.Runtime/ExtensionLoader.cs

@@ -22,7 +22,6 @@ public class ExtensionLoader
     private HashSet<string> loadedExtensions = new HashSet<string>();
 
 
-
     public ExtensionLoader(string[] packagesPaths, string unpackedExtensionsPath)
     {
         PackagesPath = packagesPaths;
@@ -34,20 +33,34 @@ public class ExtensionLoader
     {
         foreach (var packagesPath in PackagesPath)
         {
-            LoadExtensionsFromPath(packagesPath);
-        }
-    }
+            if (!Directory.Exists(packagesPath))
+            {
+                continue; // Skip if the directory does not exist
+            }
 
-    private void LoadExtensionsFromPath(string path)
-    {
-        if (!Directory.Exists(path))
-        {
-            return;
-        }
+            foreach (var updateFile in Directory.GetFiles(packagesPath, "*.update"))
+            {
+                try
+                {
+                    string newExtension = Path.ChangeExtension(updateFile, ".pixiext");
+                    if (File.Exists(newExtension))
+                    {
+                        File.Delete(newExtension);
+                    }
 
-        foreach (var file in Directory.GetFiles(path))
-        {
-            if (file.EndsWith(".pixiext"))
+                    File.Move(updateFile, newExtension);
+                }
+                catch (IOException)
+                {
+                    // File is in use, ignore
+                }
+                catch (UnauthorizedAccessException)
+                {
+                    // File is in use, ignore
+                }
+            }
+
+            foreach (var file in Directory.GetFiles(packagesPath, "*.pixiext"))
             {
                 LoadExtension(file);
             }
@@ -426,5 +439,4 @@ public class ExtensionLoader
 
         return null;
     }
-
 }

+ 2 - 2
src/PixiEditor.Extensions.WasmRuntime/WasmMemoryUtility.cs

@@ -94,8 +94,8 @@ public class WasmMemoryUtility
     public int WriteString(string value)
     {
         string valueWithNullTerminator = value + '\0';
-        var ptr = malloc.Invoke(valueWithNullTerminator.Length);
-        memory.WriteString(ptr, valueWithNullTerminator);
+        var ptr = malloc.Invoke(Encoding.UTF8.GetByteCount(valueWithNullTerminator));
+        memory.WriteString(ptr, valueWithNullTerminator, Encoding.UTF8);
         return ptr;
     }
     

+ 2 - 1
src/PixiEditor.Extensions/FlyUI/Elements/Text.cs

@@ -6,6 +6,7 @@ using Avalonia.Media;
 using PixiEditor.Extensions.CommonApi.FlyUI.Properties;
 using PixiEditor.Extensions.Extensions;
 using PixiEditor.Extensions.FlyUI.Converters;
+using PixiEditor.UI.Common.Localization;
 using FontStyle = PixiEditor.Extensions.CommonApi.FlyUI.Properties.FontStyle;
 using FontWeight = PixiEditor.Extensions.CommonApi.FlyUI.Properties.FontWeight;
 
@@ -81,7 +82,7 @@ public class Text : LayoutElement
             Converter = new ColorToAvaloniaBrushConverter(),
         };
         
-        textBlock.Bind(TextBlock.TextProperty, valueBinding);
+        textBlock.Bind(Translator.KeyProperty, valueBinding);
         textBlock.Bind(TextBlock.TextWrappingProperty, textWrapBinding);
         if (TextStyle.FontStyle != null)
         {

+ 1 - 1
src/PixiEditor.Extensions/PixiEditor.Extensions.csproj

@@ -15,7 +15,7 @@
     <ItemGroup>
       <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
       <PackageReference Include="Avalonia.Remote.Protocol" Version="$(AvaloniaVersion)" />
-      <PackageReference Include="Svg.Controls.Skia.Avalonia" Version="11.3.0.1" />
+      <PackageReference Include="Svg.Controls.Skia.Avalonia" Version="11.3.0.3" />
       <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
       <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
       <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

+ 1 - 0
src/PixiEditor.IdentityProvider.PixiAuth/PixiAuthIdentityProvider.cs

@@ -12,6 +12,7 @@ public class PixiAuthIdentityProvider : IIdentityProvider
     public string ProviderName { get; } = "PixiAuth";
     public bool AllowsLogout { get; } = true;
     public bool IsValid => isValid;
+    public string InvalidInfo { get; } = "ACCOUNT_PROVIDER_NOT_AVAILABLE";
     private bool isValid = true;
     public PixiAuthClient PixiAuthClient { get; }
     public PixiUser User { get; private set; }

+ 1 - 0
src/PixiEditor.IdentityProvider/IIdentityProvider.cs

@@ -8,6 +8,7 @@ public interface IIdentityProvider
     public bool IsLoggedIn { get; }
     public Uri? EditProfileUrl { get; }
     public bool IsValid { get; }
+    public string InvalidInfo { get; }
 
     public event Action<string, object> OnError;
     public event Action<List<ProductData>> OwnedProductsUpdated;

+ 5 - 0
src/PixiEditor.Linux/LinuxOperatingSystem.cs

@@ -43,6 +43,11 @@ public sealed class LinuxOperatingSystem : IOperatingSystem
         return true;
     }
 
+    public string[] GetAvailableRenderers()
+    {
+        return ["Vulkan", "OpenGL"];
+    }
+
     public void HandleActivatedWithFile(FileActivatedEventArgs fileActivatedEventArgs)
     {
         // TODO: Check if this is executed on Linux at all

+ 5 - 0
src/PixiEditor.MacOs/MacOperatingSystem.cs

@@ -39,4 +39,9 @@ public sealed class MacOperatingSystem : IOperatingSystem
     {
         return true;
     }
+
+    public string[] GetAvailableRenderers()
+    {
+        return ["OpenGL"];
+    }
 }

+ 2 - 0
src/PixiEditor.OperatingSystem/IOperatingSystem.cs

@@ -36,5 +36,7 @@ public interface IOperatingSystem
 
     public bool HandleNewInstance(Dispatcher? dispatcher, Action<string, bool> openInExistingAction,
         IApplicationLifetime lifetime);
+
+    public string[] GetAvailableRenderers();
 }
 

+ 7 - 7
src/PixiEditor.PixiAuth/PixiAuthClient.cs

@@ -16,7 +16,7 @@ public class PixiAuthClient
     {
         httpClient = new HttpClient();
         httpClient.BaseAddress = new Uri(baseUrl);
-        httpClient.Timeout = TimeSpan.FromSeconds(30);
+        httpClient.Timeout = Timeout.InfiniteTimeSpan;
         if (apiKey != null)
         {
             httpClient.DefaultRequestHeaders.Add("X-API-KEY", apiKey);
@@ -46,7 +46,7 @@ public class PixiAuthClient
         {
             throw new BadRequestException(await response.Content.ReadAsStringAsync());
         }
-        else if (response.StatusCode == HttpStatusCode.InternalServerError)
+        else if (response.StatusCode >= HttpStatusCode.InternalServerError)
         {
             throw new InternalServerErrorException("INTERNAL_SERVER_ERROR");
         }
@@ -101,7 +101,7 @@ public class PixiAuthClient
         {
             throw new BadRequestException(await response.Content.ReadAsStringAsync());
         }
-        else if (response.StatusCode == HttpStatusCode.InternalServerError)
+        else if (response.StatusCode >= HttpStatusCode.InternalServerError)
         {
             throw new InternalServerErrorException("INTERNAL_SERVER_ERROR");
         }
@@ -208,7 +208,7 @@ public class PixiAuthClient
                 }
             }
         }
-        else if (response.StatusCode == HttpStatusCode.InternalServerError)
+        else if (response.StatusCode >= HttpStatusCode.InternalServerError)
         {
             throw new InternalServerErrorException("INTERNAL_SERVER_ERROR");
         }
@@ -227,7 +227,7 @@ public class PixiAuthClient
             throw new BadRequestException(await response.Content.ReadAsStringAsync());
         }
 
-        if (response.StatusCode == HttpStatusCode.InternalServerError)
+        if (response.StatusCode >= HttpStatusCode.InternalServerError)
         {
             throw new InternalServerErrorException("INTERNAL_SERVER_ERROR");
         }
@@ -261,7 +261,7 @@ public class PixiAuthClient
             throw new BadRequestException(await response.Content.ReadAsStringAsync());
         }
 
-        if (response.StatusCode == HttpStatusCode.InternalServerError)
+        if (response.StatusCode >= HttpStatusCode.InternalServerError)
         {
             throw new InternalServerErrorException("INTERNAL_SERVER_ERROR");
         }
@@ -299,7 +299,7 @@ public class PixiAuthClient
             throw new BadRequestException(await response.Content.ReadAsStringAsync());
         }
 
-        if (response.StatusCode == HttpStatusCode.InternalServerError)
+        if (response.StatusCode >= HttpStatusCode.InternalServerError)
         {
             throw new InternalServerErrorException("INTERNAL_SERVER_ERROR");
         }

+ 23 - 14
src/PixiEditor.Platform.Standalone/StandaloneAdditionalContentProvider.cs

@@ -9,20 +9,20 @@ namespace PixiEditor.Platform.Standalone;
 
 public sealed class StandaloneAdditionalContentProvider : IAdditionalContentProvider
 {
-    public string ExtensionsPath { get; }
+    public string[] ExtensionsPaths { get; }
     public PixiAuthIdentityProvider IdentityProvider { get; }
 
     public event Action<string, object>? OnError;
 
-    public StandaloneAdditionalContentProvider(string extensionsPath, PixiAuthIdentityProvider identityProvider)
+    public StandaloneAdditionalContentProvider(string[] extensionsPaths, PixiAuthIdentityProvider identityProvider)
     {
         IdentityProvider = identityProvider;
-        ExtensionsPath = extensionsPath;
+        ExtensionsPaths = extensionsPaths;
     }
 
     public async Task<string?> InstallContent(string productId)
     {
-        if (!IdentityProvider.IsValid) return null;
+        if (!IdentityProvider.IsValid || ExtensionsPaths == null || ExtensionsPaths.Length == 0) return null;
 
         if (IdentityProvider.User is not { IsLoggedIn: true })
         {
@@ -35,7 +35,21 @@ public sealed class StandaloneAdditionalContentProvider : IAdditionalContentProv
                 await IdentityProvider.PixiAuthClient.DownloadProduct(IdentityProvider.User.SessionToken, productId);
             if (stream != null)
             {
-                var filePath = Path.Combine(ExtensionsPath, $"{productId}.pixiext");
+                var firstExistingPath =
+                    ExtensionsPaths.FirstOrDefault(path => File.Exists(Path.Combine(path, $"{productId}.pixiext")));
+                if (firstExistingPath != null)
+                {
+                    var updatePath = Path.Combine(firstExistingPath, $"{productId}.update");
+                    await using (var fileStream = File.Create(updatePath))
+                    {
+                        await stream.CopyToAsync(fileStream);
+                    }
+
+                    await stream.DisposeAsync();
+                    return updatePath;
+                }
+
+                var filePath = Path.Combine(ExtensionsPaths[0], $"{productId}.pixiext");
                 try
                 {
                     await using (var fileStream = File.Create(filePath))
@@ -47,7 +61,7 @@ public sealed class StandaloneAdditionalContentProvider : IAdditionalContentProv
                 }
                 catch (IOException e)
                 {
-                    filePath = Path.Combine(ExtensionsPath, $"{productId}.update");
+                    filePath = Path.Combine(ExtensionsPaths[0], $"{productId}.update");
                     await using (var fileStream = File.Create(filePath))
                     {
                         await stream.CopyToAsync(fileStream);
@@ -80,14 +94,9 @@ public sealed class StandaloneAdditionalContentProvider : IAdditionalContentProv
     {
         if (string.IsNullOrEmpty(productId)) return false;
 
-        string filePath = Path.Combine(ExtensionsPath, $"{productId}.pixiext");
-        bool exists = File.Exists(filePath);
-        if (exists) return true;
-        
-        filePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Extensions", $"{productId}.pixiext");
-        exists = File.Exists(filePath);
-
-        return exists;
+        var firstExistingPath =
+            ExtensionsPaths.FirstOrDefault(path => File.Exists(Path.Combine(path, $"{productId}.pixiext")));
+        return firstExistingPath != null;
     }
 
     public bool IsContentOwned(string product)

+ 2 - 2
src/PixiEditor.Platform.Standalone/StandalonePlatform.cs

@@ -12,11 +12,11 @@ public sealed class StandalonePlatform : IPlatform
     public IIdentityProvider? IdentityProvider { get; }
     public IAdditionalContentProvider? AdditionalContentProvider { get; }
 
-    public StandalonePlatform(string extensionsPath, string apiUrl, string? apiKey)
+    public StandalonePlatform(string[] extensionsPaths, string apiUrl, string? apiKey)
     {
         PixiAuthIdentityProvider authProvider = new PixiAuthIdentityProvider(apiUrl, apiKey);
         IdentityProvider = authProvider;
-        AdditionalContentProvider = new StandaloneAdditionalContentProvider(extensionsPath, authProvider);
+        AdditionalContentProvider = new StandaloneAdditionalContentProvider(extensionsPaths, authProvider);
     }
 
     public bool PerformHandshake()

BIN
src/PixiEditor.Platform.Steam/OSX-Linux-x64/Steamworks.NET.dll


BIN
src/PixiEditor.Platform.Steam/OSX-Linux-x64/libsteam_api.so


+ 1 - 1
src/PixiEditor.Platform.Steam/OSX-Linux-x64/steam_api.bundle/Contents/Info.plist

@@ -17,7 +17,7 @@
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleVersion</key>
-	<string>1.60</string>
+	<string>1.61</string>
 	<key>CSResourcesFileMapped</key>
 	<string>yes</string>
 	<key>DTCompiler</key>

BIN
src/PixiEditor.Platform.Steam/OSX-Linux-x64/steam_api.bundle/Contents/MacOS/libsteam_api.dylib


+ 7 - 10
src/PixiEditor.Platform.Steam/PixiEditor.Platform.Steam.csproj

@@ -4,6 +4,7 @@
     <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
+    <RuntimeIdentifiers>win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
   </PropertyGroup>
 
   <ItemGroup>
@@ -14,7 +15,7 @@
     <PackageReference Include="SkiaSharp" Version="3.116.1" />
     <PackageReference Include="Steamworks.NET" Version="2024.8.0" />
   </ItemGroup>
-
+  
   <ItemGroup Condition="'$(RuntimeIdentifier)'=='win-x64'">
     <None Include="Windows-x64\steam_api64.dll">
       <Link>steam_api64.dll</Link>
@@ -27,14 +28,14 @@
   </ItemGroup>
 
   <ItemGroup Condition="'$(RuntimeIdentifier)'=='win-arm64'">
-    <None Include="Windows-x64\steam_api64.dll">
+    <None Include="Windows-arm64\steam_api64.dll">
       <Link>steam_api64.dll</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    <None Include="Windows-x64\Steamworks.NET.dll">
+    <None Include="Windows-arm64\Steamworks.NET.dll">
       <Link>Steamworks.NET.dll</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None> 
+    </None>
   </ItemGroup>
 
   <ItemGroup Condition="'$(RuntimeIdentifier)'=='linux-x64'">
@@ -42,7 +43,6 @@
       <Link>libsteam_api.so</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    
     <None Include="OSX-Linux-x64/Steamworks.NET.dll">
       <Link>Steamworks.NET.dll</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -54,11 +54,10 @@
       <Link>libsteam_api.so</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    
     <None Include="OSX-Linux-x64/Steamworks.NET.dll">
       <Link>Steamworks.NET.dll</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None> 
+    </None>
   </ItemGroup>
 
   <ItemGroup Condition="'$(RuntimeIdentifier)'=='osx-x64'">
@@ -66,7 +65,6 @@
       <Link>steam_api.bundle</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    
     <None Include="OSX-Linux-x64/Steamworks.NET.dll">
       <Link>Steamworks.NET.dll</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -78,11 +76,10 @@
       <Link>steam_api.bundle</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </None>
-    
     <None Include="OSX-Linux-x64/Steamworks.NET.dll">
       <Link>Steamworks.NET.dll</Link>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </None> 
+    </None>
   </ItemGroup>
 
 </Project>

+ 32 - 4
src/PixiEditor.Platform.Steam/SteamIdentityProvider.cs

@@ -12,7 +12,23 @@ public class SteamIdentityProvider : IIdentityProvider
     public IUser User { get; private set; }
     public bool IsLoggedIn { get; private set; }
     public Uri? EditProfileUrl { get; } = new Uri("https://store.steampowered.com/login/");
-    public bool IsValid => true;
+
+    public bool IsValid
+    {
+        get
+        {
+            try
+            {
+                return SteamAPI.IsSteamRunning() && User != null;
+            }
+            catch (Exception ex)
+            {
+                return false;
+            }
+        }
+    }
+
+    public string InvalidInfo { get; } = "STEAM_OFFLINE";
     public event Action<string, object>? OnError;
     public event Action<List<ProductData>>? OwnedProductsUpdated;
     public event Action<string>? UsernameUpdated;
@@ -25,7 +41,10 @@ public class SteamIdentityProvider : IIdentityProvider
         var ownedContent = GetOwnedDlcs();
         var user = new SteamUser()
         {
-            Username = username, AvatarUrl = avatar, Id = id.m_SteamID, IsLoggedIn = true,
+            Username = username,
+            AvatarUrl = avatar,
+            Id = id.m_SteamID,
+            IsLoggedIn = true,
             OwnedProducts = ownedContent,
         };
 
@@ -42,6 +61,11 @@ public class SteamIdentityProvider : IIdentityProvider
 
         string cache = Path.Combine(Path.GetTempPath(), "PixiEditor", $"SteamAvatar_{id.m_SteamID}.png");
 
+        if (!Directory.Exists(Path.GetDirectoryName(cache)))
+        {
+            Directory.CreateDirectory(Path.GetDirectoryName(cache)!);
+        }
+
         bool cacheExists = File.Exists(cache);
 
         if (cacheExists)
@@ -59,7 +83,8 @@ public class SteamIdentityProvider : IIdentityProvider
                 using SKBitmap bitmap = new SKBitmap((int)width, (int)height);
                 var allocated = GCHandle.Alloc(image, GCHandleType.Pinned);
                 var info = new SKImageInfo((int)width, (int)height, SKColorType.Rgba8888, SKAlphaType.Premul);
-                bitmap.InstallPixels(info, allocated.AddrOfPinnedObject(), bitmap.RowBytes, delegate { allocated.Free(); }, null);
+                bitmap.InstallPixels(info, allocated.AddrOfPinnedObject(), bitmap.RowBytes,
+                    delegate { allocated.Free(); }, null);
                 using FileStream stream = new FileStream(cache, FileMode.Create);
                 bitmap.Encode(SKEncodedImageFormat.Png, 100).SaveTo(stream);
             }
@@ -76,8 +101,11 @@ public class SteamIdentityProvider : IIdentityProvider
         for (int i = 0; i < dlcCount; i++)
         {
             bool success = SteamApps.BGetDLCDataByIndex(i, out AppId_t appId, out bool available, out string name, 128);
-            if (success && available)
+            if (success)
             {
+                bool owned = SteamApps.BIsDlcInstalled(appId);
+                if (!owned)
+                    continue;
                 ownedDlcs.Add(new ProductData(appId.m_AppId.ToString(), name));
             }
         }

BIN
src/PixiEditor.Platform.Steam/Windows-x64/Steamworks.NET.dll


BIN
src/PixiEditor.Platform.Steam/Windows-x64/steam_api64.dll


+ 4 - 0
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -60,6 +60,8 @@
             <Color x:Key="IntSocketColor">#4C64B1</Color>
             <Color x:Key="StringSocketColor">#C9E4C6</Color>
             <Color x:Key="EllipseDataSocketColor">#a473a5</Color>
+            <Color x:Key="LineDataSocketColor">#816382</Color>
+            <Color x:Key="RectangleDataSocketColor">#825e8a</Color>
             <Color x:Key="PointsDataSocketColor">#e1d0e1</Color>
             <Color x:Key="TextDataSocketColor">#f2f2f2</Color>
             <Color x:Key="Matrix3X3SocketColor">#ffea4f</Color>
@@ -174,6 +176,8 @@
             <ConicGradientBrush x:Key="ShapeVectorDataSocketBrush"
                                 GradientStops="{StaticResource ShapeDataSocketGradient}" />
             <SolidColorBrush x:Key="EllipseVectorDataSocketBrush" Color="{StaticResource EllipseDataSocketColor}" />
+            <SolidColorBrush x:Key="LineVectorDataSocketBrush" Color="{StaticResource LineDataSocketColor}" />
+            <SolidColorBrush x:Key="RectangleVectorDataSocketBrush" Color="{StaticResource RectangleDataSocketColor}" />
             <SolidColorBrush x:Key="PointsVectorDataSocketBrush" Color="{StaticResource PointsDataSocketColor}" />
             <SolidColorBrush x:Key="TextVectorDataSocketBrush" Color="{StaticResource TextDataSocketColor}" />
 

+ 16 - 0
src/PixiEditor.UI.Common/Controls/NumberInput.cs

@@ -42,6 +42,15 @@ public partial class NumberInput : TextBox
     public static readonly StyledProperty<bool> EnableGrabberProperty = AvaloniaProperty.Register<NumberInput, bool>(
         nameof(EnableGrabber), true);
 
+    public static readonly StyledProperty<bool> DraggingGrabberProperty = AvaloniaProperty.Register<NumberInput, bool>(
+        nameof(DraggingGrabber));
+
+    public bool DraggingGrabber
+    {
+        get => GetValue(DraggingGrabberProperty);
+        private set => SetValue(DraggingGrabberProperty, value);
+    }
+
     public bool EnableGrabber
     {
         get => GetValue(EnableGrabberProperty);
@@ -211,6 +220,7 @@ public partial class NumberInput : TextBox
 
         grabber.PointerPressed += GrabberPressed;
         grabber.PointerMoved += GrabberMoved;
+        grabber.PointerReleased += GrabberReleased;
 
         return grabber;
     }
@@ -220,6 +230,7 @@ public partial class NumberInput : TextBox
         e.Pointer.Capture(leftGrabber);
         _pressedValue = Value;
         _pressedRelativeX = e.GetPosition(this).X;
+        DraggingGrabber = true;
         e.Handled = true;
     }
 
@@ -240,6 +251,11 @@ public partial class NumberInput : TextBox
         }
     }
 
+    private void GrabberReleased(object sender, PointerReleasedEventArgs e)
+    {
+        DraggingGrabber = false;
+    }
+
     private void BindTextBoxBehavior(TextBoxFocusBehavior behavior)
     {
         Binding focusNextBinding = new Binding(nameof(FocusNext)) { Source = this, Mode = BindingMode.OneWay };

+ 9 - 0
src/PixiEditor.UI.Common/Localization/Translate.cs

@@ -12,6 +12,15 @@ public class Translate : MarkupExtension
 
     public Translate()
     {
+        if (ILocalizationProvider.Current == null)
+        {
+            ILocalizationProvider.OnLocalizationProviderChanged += (provider) =>
+            {
+                ILocalizationProvider.Current.OnLanguageChanged += (lang) => LanguageChanged();
+            };
+            return;
+        }
+
         ILocalizationProvider.Current.OnLanguageChanged += (lang) => LanguageChanged();
     }
 

+ 5 - 0
src/PixiEditor.Windows/WindowsOperatingSystem.cs

@@ -92,6 +92,11 @@ public sealed class WindowsOperatingSystem : IOperatingSystem
         return false;
     }
 
+    public string[] GetAvailableRenderers()
+    {
+        return ["Vulkan", "OpenGL"];
+    }
+
     public void HandleActivatedWithFile(FileActivatedEventArgs fileActivatedEventArgs) { }
 
     public void HandleActivatedWithUri(ProtocolActivatedEventArgs openUriEventArgs) { }

+ 8 - 1
src/PixiEditor/Data/Localization/Languages/ar.json

@@ -650,5 +650,12 @@
   "MATH_MODE": "وضع الرياضيات",
   "NOISE_TYPE": "نوع الضوضاء",
   "ACTIVE_FRAME": "الإطار النشط",
-  "NORMALIZED_TIME": "الوقت الطبيعي"
+  "NORMALIZED_TIME": "الوقت الطبيعي",
+  "BETA_ANIMATIONS": "الرسوم المتحركة",
+  "SHOW_ALL_EXAMPLES": "عرض الكل",
+  "APPLY_FILTER_NODE": "تطبيق الفلتر",
+  "FILTER": "فلتر",
+  "FROM": "من",
+  "TO": "الى",
+  "TIME": "الوقت"
 }

+ 333 - 14
src/PixiEditor/Data/Localization/Languages/cs.json

@@ -135,7 +135,7 @@
   "TOGGLE_REFERENCE_LAYER_POS": "Přepnout pozici referenční vrstvy",
   "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Přepnout referenční vrstvu mezi nejvyšší nebo nejnižší",
   "RESET_REFERENCE_LAYER_POS": "Z resetovat pozici referenční vrstvy",
-  "CLIP_CANVAS": "Připni Canvas",
+  "CLIP_CANVAS": "Připni Plátno",
   "FLIP_IMG_VERTICALLY": "Překlopit obrázek svisle",
   "FLIP_IMG_HORIZONTALLY": "Překlopit obrázek vodorovně",
   "FLIP_LAYERS_VERTICALLY": "Svisle překlopit vybrané vrstvy",
@@ -149,7 +149,7 @@
   "TOGGLE_VERT_SYMMETRY_AXIS": "Přepnout vertikální osu symetrie",
   "TOGGLE_HOR_SYMMETRY_AXIS": "Zapnout osu vodorovné symetrie",
   "RESIZE_DOCUMENT": "Změnit velikost dokumentu",
-  "RESIZE_CANVAS": "Změnit velikost canvasu",
+  "RESIZE_CANVAS": "Změnit velikost plátna",
   "CENTER_CONTENT": "Vycentrovat obsah",
   "CUT": "Oříznout",
   "CUT_DESCRIPTIVE": "Oříznout vybranou oblast/vrstvy",
@@ -280,7 +280,7 @@
   "PEN_TOOL_TOOLTIP": "Pero. ({0})",
   "PEN_TOOL_ACTION_DISPLAY": "Klikni a posuň myší pro malování.",
   "PIXEL_PERFECT_SETTING": "Perfektní pixely",
-  "RECTANGLE_TOOL_TOOLTIP": "Nakreslí obdélník na canvas ({0}). Podržením klávesy Shift nakreslíte čtverec.",
+  "RECTANGLE_TOOL_TOOLTIP": "Nakreslí obdélník na plátně ({0}). Podržením klávesy Shift nakreslíte čtverec.",
   "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT": "Klikni a posuň myší pro vytvoření Obdélníku. Drž \"Shift\" pro vytvoření čtverce.",
   "RECTANGLE_TOOL_ACTION_DISPLAY_SHIFT": "Klikni a posuň myší pro vytvoření čtverce.",
   "KEEP_ORIGINAL_IMAGE_SETTING": "Ponechat originální obrázek",
@@ -296,9 +296,9 @@
   "BRIGHTNESS_TOOL_TOOLTIP": "Udělá pixely tmavší nebo světlejší. ({0}). Drž \"Ctrl\" aby dělal ty pixely tmavší.",
   "BRIGHTNESS_TOOL_ACTION_DISPLAY_DEFAULT": "Kreslením na pixely je zesvětlíš. Podrž \"Ctrl\" pro jejich ztmavnutí.",
   "BRIGHTNESS_TOOL_ACTION_DISPLAY_CTRL": "Kreslením na pixely je ztmavíš. Pusť \"Ctrl\" pro jejich zesvětlení.",
-  "COLOR_PICKER_TOOLTIP": "Vybere hlavní barvu z canvasu. ({0})",
-  "COLOR_PICKER_ACTION_DISPLAY_DEFAULT": "Klikni pro výběr barev. Podrž \"Ctrl\" abys schoval canvas. Podrž \"Shift\" pro schování referenční vrstvy.",
-  "ELLIPSE_TOOL_TOOLTIP": "Vytvoří elipsu na canvas ({0}). Podrž \"Shift\" pro přidání kružnice.",
+  "COLOR_PICKER_TOOLTIP": "Vybere hlavní barvu z plátna. ({0})",
+  "COLOR_PICKER_ACTION_DISPLAY_DEFAULT": "Klikni pro výběr barev. Podrž \"Ctrl\" abys schoval plátno. Podrž \"Shift\" pro schování referenční vrstvy.",
+  "ELLIPSE_TOOL_TOOLTIP": "Vytvoří elipsu na plátně ({0}). Podrž \"Shift\" pro přidání kružnice.",
   "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT": "Klikni a posuň myší pro vytvoření elipsy. Podrž \"Shift\" pro vytvoření kružnice.",
   "ELLIPSE_TOOL_ACTION_DISPLAY_SHIFT": "Klikni a posuň myší pro vytvoření kružnice.",
   "ERASER_TOOL_TOOLTIP": "Vygumuje barvu z pixelu. ({0})",
@@ -310,7 +310,7 @@
   "LASSO_TOOL_ACTION_DISPLAY_DEFAULT": "Klikni a posuň myší pro vybrání pixelů uvnitř vybrané oblasti lasa. Podrž \"Shift\" pro přidání do existujícího výběru. Podrž \"Ctrl\" pro odebraní z něj.",
   "LASSO_TOOL_ACTION_DISPLAY_SHIFT": "Klikni a posuň myší pro přidání pixelů dovnitř vybrané oblasti lasa.",
   "LASSO_TOOL_ACTION_DISPLAY_CTRL": "Klikni a posuň myší pro odebrání pixelů uvnitř vybrané oblasti lasa.",
-  "LINE_TOOL_TOOLTIP": "Nakreslí čáru na canvas ({0}). Podržením klávesy Shift povolíte přichytávání.",
+  "LINE_TOOL_TOOLTIP": "Nakreslí čáru na plátno ({0}). Podržením klávesy Shift povolíte přichytávání.",
   "LINE_TOOL_ACTION_DISPLAY_DEFAULT": "Klikni a posuň myší pro vytvoření čáry. Podrž \"Shift\" pro rovnoměrné čáry.",
   "LINE_TOOL_ACTION_DISPLAY_SHIFT": "Klikni a posuň myší pro vytvoření rovnoměrné čáry.",
   "MAGIC_WAND_TOOL_TOOLTIP": "Kouzelná hůlka ({0}). Vyplní výběr",
@@ -370,7 +370,7 @@
   "HORIZONTAL_LINE_SYMMETRY": "Symetrie vodorovné čáry",
   "VERTICAL_LINE_SYMMETRY": "Symetrie svislých čar",
   "COLOR_PICKER_TITLE": "Kapátko",
-  "COLOR_SLIDERS_TITLE": "Vyběrač barev",
+  "COLOR_SLIDERS_TITLE": "Vybírač barev",
   "PALETTE_TITLE": "Paleta",
   "SWATCHES_TITLE": "Vzorky",
   "LAYERS_TITLE": "Vrstvy",
@@ -545,7 +545,7 @@
   "SOURCE_UP_TO_DATE": "Zdroj je aktuální",
   "SOURCE_OLDER": "Novější cloud",
   "COLOR_PICKER_ACTION_DISPLAY_REFERENCE_ONLY": "Klikni pro výběr barvy z referenční vrstvy.",
-  "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Klikni pro výběr barvy z canvasu.",
+  "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Klikni pro výběr barvy z plátna.",
   "LOCALIZATION_DEBUG_WINDOW_TITLE": "Lokalizační Debug okno",
   "COMMAND_DEBUG_WINDOW_TITLE": "Okno Command debug",
   "SHORTCUTS_TITLE": "Zkratky",
@@ -665,9 +665,7 @@
   "ERASE_BLEND_MODE": "Vygumovat",
   "MODIFY_IMAGE_PAIR_NODE": "Upravit obrázek",
   "WITHOUT_FILTERS": "Bez filtrů",
-  "RAW_LAYER_OUTPUT": "Surový",
-  "BETA_EXAMPLE_FILES": "Ukázkové soubory z Beta verze",
-  "BETA_PROCEDURAL_GENERATION": "Procedurální animace",
+  "RAW_LAYER_OUTPUT": "Raw",
   "POND_EXAMPLE": "Jezírko",
   "TREE_EXAMPLE": "Větrný strom",
   "OUTLINE_EXAMPLE": "Automatický obrys",
@@ -778,7 +776,7 @@
   "CUSTOM_OUTPUT_NODE": "Náhledový uzel",
   "TOGGLE_HUD": "Zapnout HUD",
   "OPEN_TIMELINE": "Otevřít časovou osu",
-  "OPEN_NODE_GRAPH": "Otevřít uzlový graf",
+  "OPEN_NODE_GRAPH": "Otevřít nódový diagram",
   "TOGGLE_PLAY": "Spustit/Zastavit animaci",
   "OPEN_PREVIEW_WINDOW": "Otevřít náhled okna",
   "PREVIEW_TITLE": "Náhled",
@@ -803,5 +801,326 @@
   "EPSILON": "Epsilon",
   "PRESERVE_ALPHA": "Zachovat alfu",
   "LENGTH": "Délka",
-  "GREATER_THAN_OR_EQUAL": "Větší než nebo rovná se"
+  "GREATER_THAN_OR_EQUAL": "Větší než nebo rovná se",
+  "COLOR_MATRIX_TRANSFORM_FILTER_NODE": "Matrix transformující filtr",
+  "LERP_NODE": "Lerp",
+  "NO_PARSER_FOUND": "Nebyl nalezen žádný analyzátor souborů s příponou '{0}'",
+  "STROKE_COLOR_LABEL": "Tah",
+  "PAINT_TOOLSET": "Malování",
+  "DELETE_CELS": "Odstranit buňky",
+  "DELETE_CELS_DESCRIPTIVE": "Odstranit vybrané buňky",
+  "FILL_TYPE_WINDING": "Winding",
+  "FILL_TYPE_INVERSE_WINDING": "Obrácený Winding",
+  "STROKE_CAP": "Konec tahu",
+  "STROKE_JOIN": "Kloub tahu",
+  "COLOR_SAMPLE_MODE": "Ukázkoví režim",
+  "CREATE_CEL": "Vytvořit buňku",
+  "CREATE_CEL_DESCRIPTIVE": "Vytvořit novou buňku",
+  "DUPLICATE_CEL": "Duplikovat buňku",
+  "ROOT": "Zdroj",
+  "INVERSE_ROOT": "Obrácený zdroj",
+  "FRACTION": "Frakce",
+  "FLOOR": "Podlaha",
+  "CEIL": "Ceil",
+  "MODULO": "Modulo",
+  "STEP": "Krok",
+  "SMOOTH_STEP": "Hladký krok",
+  "BLUR_FILTER_NODE": "Filtr Gaussova rozostření",
+  "WEBP_FILE": "WebP obrázky",
+  "COLOR_NODE": "Barva",
+  "CONVERT_TO_CURVE": "Převést na křivku",
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Převést vybraný vektorovou vrstvu na křivku/cestu ",
+  "FONT_FILES": "Soubory Fontů",
+  "UNIT_PT": "pt",
+  "FONT_LABEL": "Rodina",
+  "FONT_SIZE_LABEL": "Velikost",
+  "SPACING_LABEL": "Rozestupy",
+  "TEXT_TOOL": "Text",
+  "MISSING_FONT": "Chybějící font",
+  "TEXT_LAYER_NAME": "Text",
+  "TEXT_TOOL_TOOLTIP": "Vytvořit text ({0}).",
+  "BOLD_TOOLTIP": "Tucně",
+  "ITALIC_TOOLTIP": "Kurzíva",
+  "CUSTOM_FONT": "Vlastní font",
+  "DUMP_GPU_DIAGNOSTICS": "Výpis diagnostiky GPU",
+  "USE_SRGB_PROCESSING": "Použít sRGB pro zpracovávání barev",
+  "USE_SRGB_PROCESSING_DESC": "Převést dokument s použitím lineárního sRGB na sRGB pro zpracování barev. Ovlivní to barvy dokumentu.",
+  "TEXT_NODE": "Text",
+  "TEXT_LABEL": "Text",
+  "TEXT_ON_PATH_NODE": "Text na cestě",
+  "HIGH_DPI_RENDERING": "Vysoké DPI renderování",
+  "THICKNESS": "Tlouštka",
+  "TYPE": "Psát",
+  "EFFECTS": "Efekty",
+  "OUTLINE_NODE": "Náčrt",
+  "SHADER_CODE": "Kód shaderu",
+  "SHADER_NODE": "Shadery",
+  "FAILED_TO_OPEN_EDITABLE_STRING_TITLE": "Nepodařilo se otevřít soubor",
+  "FAILED_TO_OPEN_EDITABLE_STRING_MESSAGE": "Nepodařilo se upravit tenhle řetězec v externím editoru. Důvod: {0}",
+  "STRING_EDIT_IN_DEFAULT_APP": "Upravit ve výchozí aplikaci",
+  "STRING_OPEN_IN_FOLDER": "Otevřít ve složce",
+  "DISCO_BALL_EXAMPLE": "Disko koule",
+  "COLOR_SPACE": "Oblast barev",
+  "PHOTO_EXAMPLES": "Fotka",
+  "MASK_EXAMPLE": "Maska",
+  "SHADOW_NODE": "Stínový filtr",
+  "INPUT_MATRIX": "Vstup Matrixu",
+  "OUTPUT_MATRIX": "Výstup Matrixu",
+  "CENTER": "Střed",
+  "CONTENT_OFFSET": "Odsadit obsah",
+  "CANVAS_POSITION": "Pozice plátna",
+  "CENTER_POSITION": "Vycentrovat pozici",
+  "TILE_MODE_X": "Režim dlaždic X",
+  "TILE_MODE_Y": "Režim dlaždic Y",
+  "TILE_NODE": "Dlaždice",
+  "SKEW": "Zkosení",
+  "OFFSET_NODE": "Odsazení",
+  "SKEW_NODE": "Zkosení",
+  "ROTATION_NODE": "Otáčení",
+  "SCALE_NODE": "Měřítko",
+  "ROTATE_NODE": "Otočit",
+  "TRANSFORM_NODE": "Transformovat",
+  "UNIT": "Jednotka",
+  "ANGLE": "Úhel",
+  "DOCUMENT_INFO_NODE": "Informace o dokumentu",
+  "MASK_NODE": "Maska",
+  "SEPIA_FILTER_NODE": "Sepia filtr",
+  "INTENSITY": "Intenzita",
+  "INVERT_FILTER_NODE": "Invertovat filtr",
+  "COLOR_ADJUSTMENTS_FILTER": "Filtr pro úpravu barev",
+  "ADJUST_BRIGHTNESS": "Upravit jas",
+  "ADJUST_CONTRAST": "Upravit kontrast",
+  "ADJUST_SATURATION": "Upravit saturaci",
+  "ADJUST_TEMPERATURE": "Upravit teplotu",
+  "ADJUST_TINT": "Upravit tón",
+  "ADJUST_HUE": "Upravit odstín",
+  "HUE_VALUE": "Hue",
+  "SATURATION_VALUE": "Saturace",
+  "BRIGHTNESS_VALUE": "Jas",
+  "CONTRAST_VALUE": "Kontrast",
+  "TEMPERATURE_VALUE": "Teplota",
+  "TINT_VALUE": "Tón",
+  "FAILED_DOWNLOADING_UPDATE_TITLE": "Nepodařilo se stáhnout update",
+  "FAILED_DOWNLOADING_UPDATE": "Nepodařilo se stáhnout update. Zkuste to znovu později.",
+  "DELETE_SELECTED": "Odstranit vybrané",
+  "DELETE_SELECTED_DESCRIPTIVE": "Odstranit vybraný prvek (vrstvu, pixely, atd.)",
+  "GRIDLINES_SIZE": "Velikost mřížky",
+  "CANVAS": "Plátno",
+  "UNEXPECTED_SHUTDOWN": "Neočekávané vypnutí",
+  "UNEXPECTED_SHUTDOWN_MSG": "PixiEditor byl neočekávaně vypnut. Načetli jsme poslední autosave z vašich souborů.",
+  "OK": "OK",
+  "OPEN_AUTOSAVES": "Prohlížet autosavy",
+  "AUTOSAVE_SETTINGS_HEADER": "Autosave",
+  "AUTOSAVE_SETTINGS_SAVE_STATE": "Znovuotevřít poslední soubory při spuštění",
+  "AUTOSAVE_SETTINGS_PERIOD": "Lhůta autosavu",
+  "AUTOSAVE_ENABLED": "Autosave zapnut",
+  "MINUTE_UNIVERSAL": "min",
+  "AUTOSAVE_SETTINGS_SAVE_USER_FILE": "Autosavnout do vybraného souboru",
+  "LOAD_LAZY_FILE_MESSAGE": "Ke zkrácení času spuštění, PixiEditor nenačetl tenhle soubor. Klikněte na tlačítko pod ním k jeho načtení.",
+  "EASING_NODE": "Zmírnění",
+  "EASING_TYPE": "Typ zmírnění",
+  "OPEN_DIRECTORY_ON_EXPORT": "Otevřít složku při exportu",
+  "ERROR_LOOP_DETECTED_MESSAGE": "Pohnutím téhle vrstvy vytvoří smyčku. Opravte to v Nódovém diagramu",
+  "LINEAR_EASING_TYPE": "Linear",
+  "IN_SINE_EASING_TYPE": "In Sine",
+  "OUT_SINE_EASING_TYPE": "Out Sine",
+  "IN_OUT_SINE_EASING_TYPE": "In Out Sine",
+  "IN_QUAD_EASING_TYPE": "In Quad",
+  "OUT_QUAD_EASING_TYPE": "Out Quad",
+  "IN_OUT_QUAD_EASING_TYPE": "In Out Quad",
+  "IN_CUBIC_EASING_TYPE": "In Cubic",
+  "OUT_CUBIC_EASING_TYPE": "Out Cubic",
+  "IN_OUT_CUBIC_EASING_TYPE": "In Out Cubic",
+  "IN_QUART_EASING_TYPE": "In Quart",
+  "OUT_QUART_EASING_TYPE": "Out Quart",
+  "IN_OUT_QUART_EASING_TYPE": "In Out Quart",
+  "IN_QUINT_EASING_TYPE": "In Quint",
+  "OUT_QUINT_EASING_TYPE": "Out Quint",
+  "IN_OUT_QUINT_EASING_TYPE": "In Out Quint",
+  "IN_EXPO_EASING_TYPE": "In Expo",
+  "OUT_EXPO_EASING_TYPE": "Out Expo",
+  "IN_OUT_EXPO_EASING_TYPE": "In Out Expo",
+  "IN_CIRC_EASING_TYPE": "In Circ",
+  "OUT_CIRC_EASING_TYPE": "Out Circ",
+  "IN_OUT_CIRC_EASING_TYPE": "In Out Circ",
+  "IN_BACK_EASING_TYPE": "In Back",
+  "OUT_BACK_EASING_TYPE": "Out Back",
+  "IN_OUT_BACK_EASING_TYPE": "In Out Back",
+  "IN_ELASTIC_EASING_TYPE": "In Elastic",
+  "OUT_ELASTIC_EASING_TYPE": "Out Elastic",
+  "IN_OUT_ELASTIC_EASING_TYPE": "In Out Elastic",
+  "IN_BOUNCE_EASING_TYPE": "In Bounce",
+  "OUT_BOUNCE_EASING_TYPE": "Out Bounce",
+  "IN_OUT_BOUNCE_EASING_TYPE": "In Out Bounce",
+  "CLAMP_SHADER_TILE_NODE": "Svorka",
+  "REPEAT_SHADER_TILE_NODE": "Opakovat",
+  "MIRROR_SHADER_TILE_NODE": "Zrcadlo",
+  "DECAL_SHADER_TILE_NODE": "Obtisk",
+  "R_G_B_COMBINE_SEPARATE_COLOR_MODE": "RGB",
+  "H_S_V_COMBINE_SEPARATE_COLOR_MODE": "HSV",
+  "H_S_L_COMBINE_SEPARATE_COLOR_MODE": "HSL",
+  "COLOR_MANAGED_COLOR_SAMPLE_MODE": "Spravovaná barva",
+  "RAW_COLOR_SAMPLE_MODE": "Raw",
+  "FRACTAL_PERLIN_NOISE_TYPE": "Perlin",
+  "TURBULENCE_PERLIN_NOISE_TYPE": "Turbulence",
+  "INHERIT_COLOR_SPACE_TYPE": "Zdědit",
+  "SRGB_COLOR_SPACE_TYPE": "sRGB",
+  "LINEAR_SRGB_COLOR_SPACE_TYPE": "Lineární sRGB",
+  "SIMPLE_OUTLINE_TYPE": "Jednoduchý",
+  "GAUSSIAN_OUTLINE_TYPE": "Gaussian",
+  "PIXEL_PERFECT_OUTLINE_TYPE": "Pixelově perfektní",
+  "DEGREES_ROTATION_TYPE": "Stupně",
+  "RADIANS_ROTATION_TYPE": "Radiany",
+  "WEIGHTED_GRAYSCALE_MODE": "Weighted",
+  "AVERAGE_GRAYSCALE_MODE": "Průměr",
+  "CUSTOM_GRAYSCALE_MODE": "Vlastní",
+  "CLAMP_TILE_MODE": "Svorka",
+  "REPEAT_TILE_MODE": "Opakovat",
+  "MIRROR_TILE_MODE": "Zrcadlo",
+  "DECAL_TILE_MODE": "Obtisk",
+  "ERR_UNKNOWN_FILE_FORMAT": "Neznámí typ souboru",
+  "ERR_EXPORT_SIZE_INVALID": "Nesprávná velikost exportu. Hodnoty musí být větší než 0.",
+  "ERR_UNKNOWN_IMG_FORMAT": "Neznámí formát obrázku '{0}'.",
+  "ERR_FAILED_GENERATE_SPRITE_SHEET": "Nepodařilo se vygenerovat sprite sheet",
+  "ERR_NO_RENDERER": "Renderer animcí nebyl nalezen",
+  "ERR_RENDERING_FAILED": "Renderování se nepodařilo",
+  "ENABLE_ANALYTICS": "Posílat anonymní analytická data",
+  "ANALYTICS_INFO": "Sbíráme anonymní data k vylepšování PixiEditoru. Žádná osobní data nejsou sbíraná.",
+  "LANGUAGE_INFO": "Všechny překlady jsou udělané komunitou. Připojte se na náš discord server pro více informací.",
+  "UP_TO_DATE_UNKNOWN": "Nebyli jsme schopni zkontrolovat aktualizace",
+  "UP_TO_DATE": "Používáte nejnovější verzi",
+  "UPDATE_AVAILABLE": "Aktualizace {0} je k dispozici",
+  "CHECKING_UPDATES": "Kontrola aktualizací...",
+  "UPDATE_FAILED_DOWNLOAD": "Nepovedlo se stáhnout aktualizaci",
+  "UPDATE_READY_TO_INSTALL": "Aktualizace je připravena. Přepnout na {0}?",
+  "SWITCH_TO_NEW_VERSION": "Přepnout",
+  "DOWNLOAD_UPDATE": "Stáhnout",
+  "CHECKING_FOR_UPDATES": "Kontrola aktualizací...",
+  "PAINT_SHAPE_SETTING": "Tvar štětce",
+  "BOOL_OPERATION_NODE": "Logická operace",
+  "FIRST_SHAPE": "První tvar",
+  "SECOND_SHAPE": "Druhý tvar",
+  "OPERATION": "Operace",
+  "UNION_VECTOR_PATH_OP": "Unie",
+  "DIFFERENCE_VECTOR_PATH_OP": "Rozdíl",
+  "INTERSECT_VECTOR_PATH_OP": "Průsečík",
+  "XOR_VECTOR_PATH_OP": "XOR",
+  "REVERSE_DIFFERENCE_VECTOR_PATH_OP": "Obrátit rozdíl",
+  "NO_DOCUMENT_OPEN": "Nic tu není",
+  "EMPTY_DOCUMENT_ACTION_BTN": "Začít vytvářet",
+  "ONBOARDING_TITLE": "Vítejte v",
+  "ONBOARDING_DESCRIPTION": "Pojďme nastavit vaši pracovní plochu!",
+  "ONBOARDING_SKIP_BTN": "Přeskočit",
+  "ONBOARDING_ACTION_BTN": "Pojďme začít",
+  "ONB_SELECT_PRIMARY_TOOLSET": "Vyberte svůj hlavní Set nástrojů",
+  "ONB_NEXT_BTN": "Další",
+  "ONB_BACK_BTN": "Předchozí",
+  "ONB_ANALYTICS": "Anonymní analytická data",
+  "ONB_ALL_SET": "Vše je připravené!",
+  "ONB_ALL_SET_BTN": "Začít vytvářet",
+  "ANALYTICS_INFO_DETAILED": "PixiEditor sbírá anonymní data k vylepšení aplikace. Tyto data nezahrnují žádné osobní údaje. PixiEditor sbírá data o tom:\n- Jaké nástroje jsou používány a jak\n- Jak dlouho jste aplikaci používali\n- Jaké příkazy jsou používány\n- Údaje o výkonu\n\nMůžete vypnout sbírání analytických dat kdykoliv v nastavení.",
+  "PRIVACY_POLICY": "Privacy Policy",
+  "ONB_SHORTCUTS": "Vyberte své klávesové zkrataky",
+  "GRAPH_STATE_UNABLE_TO_CREATE_MEMBER": "Aktuální nastavení Nódového diagramu nedovoluje vytváření nových vrstev vedle vrstvy vybrané. ",
+  "PRIMARY_TOOLSET": "Hlavní Set nástrojů",
+  "OPEN_ONBOARDING_WINDOW": "Otevřít vstupní okno",
+  "AUTO_SCALE_BACKGROUND": "Automaticky zvětšit pozadí",
+  "UPDATES": "Aktualizace",
+  "SCENE": "Scéna",
+  "CUSTOM_BACKGROUND_SCALE": "Vlastní zvětšení pozadí",
+  "PRIMARY_BG_COLOR": "Hlavní barva pozadí",
+  "SECONDARY_BG_COLOR": "Druhotná barva pozadí",
+  "RESET": "Resetovat",
+  "AUTOSAVE_OPEN_FOLDER": "Otevřít autosave složku",
+  "AUTOSAVE_OPEN_FOLDER_DESCRIPTIVE": "Otevři složku kde autosavy jsou uložené",
+  "AUTOSAVE_TOGGLE_DESCRIPTIVE": "Zapnout/vypnout autosave",
+  "OPEN_TYPE_FONT": "Fonty OpenType",
+  "TRUE_TYPE_FONT": "Fonty TrueType",
+  "SVG_FILE": "Škálovatelná vektorová grafika",
+  "EXAMPLE_FILES": "Názorné soubory",
+  "PROCEDURAL_GENERATION": "Procedurální animace",
+  "ERROR_GRAPH": "Při nastavení diagramu nastala chyba. Opravte to v nódovém diagramu",
+  "COLOR_MATRIX_FILTER_NODE": "Barevný Matrix filtr",
+  "WORKSPACE": "Pracovní plocha",
+  "EXPORT_ZONE_NODE": "Exportovací zóna",
+  "IS_DEFAULT_EXPORT": "Je Výchozí Export",
+  "EXPORT_OUTPUT": "Exportovat výstup",
+  "RENDER_OUTPUT_SIZE": "Výstupní velikost renderování",
+  "RENDER_OUTPUT_CENTER": "Výstupní centrum renderování",
+  "COLOR_PICKER": "Vybírač barev",
+  "UNAUTHORIZED_ACCESS": "Neoprávněný přístup",
+  "SEPARATE_SHAPES": "Rozdělit tvary",
+  "SEPARATE_SHAPES_DESCRIPTIVE": "Rozdělit tvary ze současného vektoru do jednotlivých vrstev",
+  "TEXT": "Text",
+  "EXTRACT_SELECTED_TEXT": "Extrahovat vybraný text",
+  "EXTRACT_SELECTED_TEXT_DESCRIPTIVE": "Extrahovat vybraný text do nové vrstvy.",
+  "EXTRACT_SELECTED_CHARACTERS": "Extrahovat vybrané znaky",
+  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Extrahovat jednotlivé znaky z výběru do nových vrstev.",
+  "STEP_START": "Jít dozadu k nejbližší buňce",
+  "STEP_END": "Jít dopředu k nejbližší buňce",
+  "STEP_FORWARD": "Jít dopředu o jeden snímek",
+  "STEP_BACK": "Jít zpět o jeden snímek",
+  "ONB_FINISH_BTN": "Dokončit",
+  "USER_NOT_FOUND": "Prosím zadejte email použitý ke koupi Sponzorské edice.",
+  "SESSION_NOT_VALID": "Relace není platná, prosím přihlaste se znovu",
+  "SESSION_NOT_FOUND": "Relace nebyla nalezena, zkuste se přihlásit znovu",
+  "INTERNAL_SERVER_ERROR": "Nastala chyba v serveru. Prosím zkuste to znovu později.",
+  "TOO_MANY_REQUESTS": "Až moc požadavků. Zkuste znovu za {0} sekund.",
+  "SESSION_EXPIRED": "Relace skončila. Přihlaste se znovu.",
+  "CONNECTION_ERROR": "Chyba připojení. Prosím zkontrolujte internetové připojení.",
+  "FAIL_LOAD_USER_DATA": "Nepodařilo se načíst uložená data",
+  "LOGOUT": "Odhlásit se",
+  "LOGGED_IN_AS": "Výtejte",
+  "EMAIL_SENT": "Email byl poslán! Zkontrolujte si emailovou schránku.",
+  "RESEND_ACTIVATION": "Znovu poslat",
+  "INVALID_TOKEN": "Relace je neplatná nebo její platnost vypršela. Přihlaste se znovu.",
+  "ENTER_EMAIL": "Zadejte svůj email",
+  "LOGIN_LINK": "Poslat přihlašovací odkaz",
+  "LOGIN_LINK_INFO": "Pošleme vám email s bezpečným odkazem k přihlášení. Není třeba žádného hesla.",
+  "ACCOUNT_WINDOW_TITLE": "Účet",
+  "CONNECTION_TIMEOUT": "Čas pro připojení vypršel. Zkuste to prosím znovu.",
+  "OPEN_ACCOUNT_WINDOW": "Spravovat účet",
+  "INSTALL": "Instalovat",
+  "MANAGE_ACCOUNT": "Spravovat",
+  "OWNED_PRODUCTS": "Vlastněný obsah",
+  "INSTALLING": "Instaluje se",
+  "INSTALLED": "Nainstalováno",
+  "ACCOUNT_PROVIDER_INFO": "Účet spravuje",
+  "UPDATE": "Aktualizace",
+  "FOUNDERS_BUNDLE": "Sponzorský balíček",
+  "FOUNDERS_BUNDLE_SUBTEXT": "Podpořte PixiEditor a zvětšete svou produktivitu!",
+  "BECOME_A_FOUNDER": "Staňte se sponzorem",
+  "LOGIN": "Přihlásit se",
+  "NOT_FOUNDER_YET": "Stále nejsi sponzorem?",
+  "ANIMATION_QUALITY_PRESET": "Předvolba kvality",
+  "VERY_LOW_QUALITY_PRESET": "Velmi nízká",
+  "LOW_QUALITY_PRESET": "Nízká",
+  "MEDIUM_QUALITY_PRESET": "Střední",
+  "HIGH_QUALITY_PRESET": "Vysoká",
+  "VERY_HIGH_QUALITY_PRESET": "Velmi vysoká",
+  "EXPORT_FRAMES": "Exportovat snímky",
+  "NORMALIZE_OFFSET": "Normalizovat odsazení",
+  "TANGENT": "Tangent",
+  "EVALUATE_PATH_NODE": "Vyhodnotit cestu",
+  "OLD_MIN": "Starý Min",
+  "OLD_MAX": "Starý Max",
+  "NEW_MIN": "Nový Min",
+  "NEW_MAX": "Nový Max",
+  "REMAP_NODE": "Přemapovat",
+  "TEXT_TOOL_ACTION_DISPLAY": "Klikni na plátno k přidání nového textu (Posouvej s myší během klikání k nastavení velikosti). Klikni na existující text k jeho úpravě.",
+  "PASTE_CELS": "Vložit buňky",
+  "SCALE_X": "Měřítko X",
+  "SCALE_Y": "Měřítko Y",
+  "TRANSLATE_X": "Přeložit X",
+  "TRANSLATE_Y": "Přeložit Y",
+  "SKEW_X": "Zkosení X",
+  "SKEW_Y": "Zkosení Y",
+  "PERSPECTIVE_0": "Perspektiva 0",
+  "PERSPECTIVE_1": "Perspektiva 1",
+  "PERSPECTIVE_2": "Perspektiva 2",
+  "COMPOSE_MATRIX": "Složit Matrix",
+  "DECOMPOSE_MATRIX": "Rozložit Matrix",
+  "NORMALIZE_COORDINATES": "Znormalizovat souřadnice",
+  "TRANSFORMED_POSITION": "Transformovat pozici",
+  "ACCOUNT_PROVIDER_NOT_AVAILABLE": "Tato verze PixiEditoru nepodporuje účty. Používejte oficiální verzi z pixieditor.net ke spravování vašeho účtu."
 }

+ 380 - 4
src/PixiEditor/Data/Localization/Languages/de.json

@@ -289,7 +289,7 @@
   "SELECT_TOOL_TOOLTIP": "Wählt Bereich aus. ({0})",
   "SELECT_TOOL_ACTION_DISPLAY_DEFAULT": "Klicke und bewegen die Maus um einen Bereich auszuwählen. Halte die Umschalttaste/Shift um zu der existierenden Auswahl hinzuzufügen. Halte Strg um von ihr zu entfernen.",
   "SELECT_TOOL_ACTION_DISPLAY_SHIFT": "Klicke und bewegen die Maus um den Bereich zur aktuellen Auswahl hinzuzufügen.",
-  "SELECT_TOOL_ACTION_DISPLAY_CTRL": "Clicke und Bewege die Maus um den Bereich von der aktuellen Auswahl zu löschen.",
+  "SELECT_TOOL_ACTION_DISPLAY_CTRL": "Klicke und Bewege die Maus um den Bereich von der aktuellen Auswahl zu löschen.",
   "ZOOM_TOOL_TOOLTIP": "Zoomt Anzeigebereich ({0}). Klicke um rein zu zoomen, halte Strg und Klicke um raus zu zoomen.",
   "ZOOM_TOOL_ACTION_DISPLAY_DEFAULT": "Klicke und bewegen die Maus um zu zoomen. Klicke nur, um rein zu zoomen. Halte Strg und Klicke um raus zu zoomen.",
   "ZOOM_TOOL_ACTION_DISPLAY_CTRL": "Klicke und bewegen die Maus um zu zoomen. Klicke nur, um raus zu zoomen. Lasse Strg los, um Klicke nur, um rein zu zoomen",
@@ -665,8 +665,6 @@
   "MODIFY_IMAGE_PAIR_NODE": "Bild bearbeiten",
   "WITHOUT_FILTERS": "Ohne Filter",
   "RAW_LAYER_OUTPUT": "Ohne alles",
-  "BETA_EXAMPLE_FILES": "Beta Beispielbilder",
-  "BETA_PROCEDURAL_GENERATION": "Prozedurale Animation",
   "POND_EXAMPLE": "Teich",
   "TREE_EXAMPLE": "Baum im Wind",
   "OUTLINE_EXAMPLE": "Automatische Kontur",
@@ -736,5 +734,383 @@
   "HARDNESS_SETTING": "Stärke",
   "SPACING_SETTING": "Abstand",
   "ANTI_ALIASING_SETTING": "Kantenglättung",
-  "TOLERANCE_LABEL": "Toleranz"
+  "TOLERANCE_LABEL": "Toleranz",
+  "GENERATING_SPRITE_SHEET": "Sprite Sheet wird erstellt",
+  "RASTERIZE_SHAPE": "Form rasterisieren",
+  "TOGGLE_ANIMATION": "Animation ein/ausschalten",
+  "OFFSET": "Verschiebung",
+  "STRUCTURE": "Struktur",
+  "STROKE_COLOR_LABEL": "Kontur",
+  "SYNC_WITH_PRIMARY_COLOR_LABEL": "Mit primärer Farbe synchronisieren",
+  "RASTERIZE": "Rasterisieren",
+  "RASTERIZE_ACTIVE_LAYER": "Aktive Ebene rasterisieren",
+  "RASTERIZE_ACTIVE_LAYER_DESCRIPTIVE": "Aktive Ebene in Rasterebene rasterisieren.",
+  "RENDER_OUTPUT": "Gerendert",
+  "TOGGLE_SNAPPING": "An Raster halten",
+  "HIGH_RES_PREVIEW": "Maximale Auflösung",
+  "LOW_RES_PREVIEW": "Dokumenten Auflösung",
+  "TOGGLE_HIGH_RES_PREVIEW": "Maximale Auflösung ein/ausschalten",
+  "FACTOR": "Faktor",
+  "PATH_TOOL": "Pfad",
+  "PATH_TOOL_TOOLTIP": "Erstellt Pfade und Kurven ({0}).",
+  "PATH_TOOL_ACTION_DISPLAY": "Klicke um einen Punkt hinzuzufügen.",
+  "PATH_TOOL_ACTION_DISPLAY_CTRL": "Klicke auf existierenden Punkt und ziehen ihn, um eine Kurve zu erstellen. Klicke auf einen Kontrollpunkt um ihn auszuwählen.",
+  "PATH_TOOL_ACTION_DISPLAY_SHIFT": "Klicke um eine neue Ebene zu erstellen.",
+  "PATH_TOOL_ACTION_DISPLAY_CTRL_SHIFT": "Klicke auf einen Kontrollpunkt um ihn zu der Auswahl hinzuzufügen.",
+  "PATH_TOOL_ACTION_DISPLAY_ALT": "Klicke auf einen Kontrollpunkt und bewege ihn um nur eine Seite der Kurve zu bearbeiten.",
+  "DEFAULT_PATH_LAYER_NAME": "Pfad",
+  "DELETE_NODES": "Knoten löschen",
+  "DELETE_NODES_DESCRIPTIVE": "Ausgewählte Knoten löschen",
+  "DELETE_CELS": "Zelle löschen",
+  "DELETE_CELS_DESCRIPTIVE": "Ausgewählte Folien löschen",
+  "COPY_COLOR_TO_CLIPBOARD": "Farbe in Zwischenablage kopieren",
+  "VIEWPORT_ROTATION": "Rotation der Ansicht",
+  "NEXT_TOOL_SET": "Nächstes Werkzeugset",
+  "PREVIOUS_TOOL_SET": "Vorheriges Werkzeugset",
+  "FILL_MODE": "Füllmodus",
+  "USE_LINEAR_SRGB_PROCESSING": "Lineares sRGB verwenden",
+  "FILL_TYPE_WINDING": "Nicht null",
+  "FILL_TYPE_EVEN_ODD": "Gerade/Ungerade",
+  "FILL_TYPE_INVERSE_WINDING": "Invertiert: Nicht null",
+  "FILL_TYPE_INVERSE_EVEN_ODD": "Invertiert: Gerade/Ungerade",
+  "STROKE_CAP": "Endkappe",
+  "STROKE_JOIN": "Eckenart",
+  "COPY_VISIBLE": "Sichtbare Kopieren",
+  "COPY_VISIBLE_DESCRIPTIVE": "Sichtbare Pixel kopieren",
+  "COLOR_SAMPLE_MODE": "Farbraum Modus",
+  "CREATE_CEL": "Zelle erstellen",
+  "CREATE_CEL_DESCRIPTIVE": "Neue Zelle erstellen",
+  "DUPLICATE_CEL": "Zelle duplizieren",
+  "DUPLICATE_CEL_DESCRIPTIVE": "Zelle in aktuellem Frame duplizieren",
+  "OUTPUT_NAME": "Name",
+  "CUSTOM_OUTPUT_NODE": "Individuelles Ergebnis",
+  "TOGGLE_HUD": "HUD ein/ausschalten",
+  "OPEN_TIMELINE": "Zeitleiste öffnen",
+  "OPEN_NODE_GRAPH": "Knotenansicht öffnen",
+  "TOGGLE_PLAY": "Animation abspielen/pausieren",
+  "OPEN_PREVIEW_WINDOW": "Vorschaufenster öffnen",
+  "PREVIEW_TITLE": "Vorschau",
+  "GREATER_THAN": "Größer als",
+  "LESS_THAN": "Kleiner als",
+  "LESS_THAN_OR_EQUAL": "Kleiner-gleich als",
+  "COMPARE": "Vergleichen",
+  "MATH_POWER": "Potenzieren",
+  "LOGARITHM": "Logarithmus",
+  "NATURAL_LOGARITHM": "Natürlicher Logarithmus",
+  "ROOT": "Wurzel",
+  "INVERSE_ROOT": "Invertierte Wurzel",
+  "FRACTION": "Fraktion",
+  "NEGATE": "Negieren",
+  "FLOOR": "Abrunden",
+  "CEIL": "Aufrunden",
+  "ROUND": "Runden",
+  "MODULO": "Rest",
+  "STEP": "Schritt",
+  "SMOOTH_STEP": "Sanfte Interpolation",
+  "COPY_NODES": "Knoten kopieren",
+  "COPY_NODES_DESCRIPTIVE": "Ausgewählte Knoten kopieren",
+  "PASTE_NODES": "Knoten einfügen",
+  "PASTE_NODES_DESCRIPTIVE": "Kopierte Knoten einfügen",
+  "COPY_CELS": "Zellen kopieren",
+  "COPY_CELS_DESCRIPTIVE": "Ausgewählte Zellen kopieren",
+  "TOGGLE_ONION_SKINNING_DESCRIPTIVE": "Zwiebelhäute umschalten",
+  "VALUE": "Wert",
+  "TARGET": "Ziel",
+  "EPSILON": "Epsilon",
+  "PRESERVE_ALPHA": "Alpha fixieren",
+  "BLUR_FILTER_NODE": "Gaußscher Weichzeichner Filter",
+  "LENGTH": "Länge",
+  "GREATER_THAN_OR_EQUAL": "Größer-gleich als",
+  "WEBP_FILE": "WebP Bilder",
+  "COLOR_NODE": "Farbe",
+  "CONVERT_TO_CURVE": "In Pfad umwandeln",
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Ausgewählte Vektorebene in Pfad umwandeln",
+  "FONT_FILES": "Schriftart Datein",
+  "UNIT_PT": "pt",
+  "FONT_LABEL": "Familie",
+  "FONT_SIZE_LABEL": "Schriftgröße",
+  "SPACING_LABEL": "Zeilenabstand",
+  "TEXT_TOOL": "Text",
+  "MISSING_FONT": "Fehlende Schriftart",
+  "TEXT_LAYER_NAME": "Text",
+  "TEXT_TOOL_TOOLTIP": "Erstellt Text ({0}).",
+  "BOLD_TOOLTIP": "Fett",
+  "ITALIC_TOOLTIP": "Kursiv",
+  "CUSTOM_FONT": "Benutzerdefinierte Schriftart",
+  "DUMP_GPU_DIAGNOSTICS": "Dump GPU diagnostics",
+  "USE_SRGB_PROCESSING": "sRGB für Farbverarbeitung verweden",
+  "USE_SRGB_PROCESSING_DESC": "Lineares sRGB in sRGB umwandeln für Farbverarbeitung. Dies wird die Farben im Dokument beeinflussen.",
+  "TEXT_NODE": "Text",
+  "TEXT_LABEL": "Text",
+  "TEXT_ON_PATH_NODE": "Text auf Pfad",
+  "HIGH_DPI_RENDERING": "Rendering mit hoher DPI",
+  "THICKNESS": "Dicke",
+  "TYPE": "Typ",
+  "EFFECTS": "Effekte",
+  "OUTLINE_NODE": "Umrandung",
+  "SHADER_CODE": "Shadercode",
+  "SHADER_NODE": "Shader",
+  "FAILED_TO_OPEN_EDITABLE_STRING_TITLE": "Fehler beim öffnen der Datei",
+  "FAILED_TO_OPEN_EDITABLE_STRING_MESSAGE": "Fehler beim bearbeiten des Textes im externen Editor. Grund: {0}",
+  "STRING_EDIT_IN_DEFAULT_APP": "In Standardapp Editieren",
+  "STRING_OPEN_IN_FOLDER": "In Ordner öffnen",
+  "DISCO_BALL_EXAMPLE": "Disco Ball",
+  "COLOR_SPACE": "Farbraum",
+  "PHOTO_EXAMPLES": "Foto",
+  "MASK_EXAMPLE": "Maske",
+  "SHADOW_NODE": "Schatten Filter",
+  "INPUT_MATRIX": "Matrix",
+  "OUTPUT_MATRIX": "Matrix Ergebnis",
+  "CENTER": "Mittelpunkt",
+  "CONTENT_OFFSET": "Inhalt Abstand",
+  "CANVAS_POSITION": "Leinwand Position",
+  "CENTER_POSITION": "Zentrum",
+  "TILE_MODE_X": "Spiegelmodus X",
+  "TILE_MODE_Y": "Spiegelmodus Y",
+  "TILE_NODE": "Spiegeln",
+  "SKEW": "Verzerren",
+  "OFFSET_NODE": "Verschieben",
+  "SKEW_NODE": "Verzerren",
+  "ROTATION_NODE": "Rotieren",
+  "SCALE_NODE": "Skalieren",
+  "ROTATE_NODE": "Rotieren",
+  "TRANSFORM_NODE": "Transformation",
+  "UNIT": "Einheit",
+  "ANGLE": "Winkel",
+  "DOCUMENT_INFO_NODE": "Dokument Info",
+  "MASK_NODE": "Maske",
+  "SEPIA_FILTER_NODE": "Sepia Filter",
+  "INTENSITY": "Intensität",
+  "INVERT_FILTER_NODE": "Invertierungsfilter",
+  "COLOR_ADJUSTMENTS_FILTER": "Farbanpassungsfilter",
+  "ADJUST_BRIGHTNESS": "Helligkeit anpassen",
+  "ADJUST_CONTRAST": "Kontrast anpassen",
+  "ADJUST_SATURATION": "Sättigung anpassen",
+  "ADJUST_TEMPERATURE": "Temperatur anpassen",
+  "ADJUST_TINT": "Ton anpassen",
+  "ADJUST_HUE": "Farbe anpassen",
+  "HUE_VALUE": "Hue",
+  "SATURATION_VALUE": "Sättigung",
+  "BRIGHTNESS_VALUE": "Helligkeit",
+  "CONTRAST_VALUE": "Kontrast",
+  "TEMPERATURE_VALUE": "Temperatur",
+  "TINT_VALUE": "Ton",
+  "FAILED_DOWNLOADING_UPDATE_TITLE": "Fehler beim herunterladen",
+  "FAILED_DOWNLOADING_UPDATE": "Fehler beim herunterladen des Updates. Versuche es später erneut.",
+  "DELETE_SELECTED": "Ausgewählte Löschen",
+  "DELETE_SELECTED_DESCRIPTIVE": "Ausgewählte Elemente löschen (ebenen, pixel, usw...)",
+  "GRIDLINES_SIZE": "Gittergröße",
+  "CANVAS": "Leinwand",
+  "UNEXPECTED_SHUTDOWN": "Unerwartetes Beenden",
+  "UNEXPECTED_SHUTDOWN_MSG": "PixiEditor wurde unerwartet beendet. Wir haben die letzten automatisch gespeicherten Dateien wieder geladen.",
+  "OK": "Ok",
+  "OPEN_AUTOSAVES": "Alle Dateien öffnen",
+  "AUTOSAVE_SETTINGS_HEADER": "Automatisches Speichern",
+  "AUTOSAVE_SETTINGS_SAVE_STATE": "Zuletzt offene Dateien bei Programmstart wieder öffnen",
+  "AUTOSAVE_SETTINGS_PERIOD": "Intervall für automatisches Speichern",
+  "AUTOSAVE_ENABLED": "Automatisches Speichern aktivieren",
+  "MINUTE_UNIVERSAL": "min",
+  "AUTOSAVE_SETTINGS_SAVE_USER_FILE": "Automatisches Speichern an Speicherort speichern",
+  "LOAD_LAZY_FILE_MESSAGE": "Um die Startzeit zu verbessern, hat PixiEditor diese Datei nicht geladen. Klicke auf den unteren Button um sie zu laden.",
+  "EASING_NODE": "Interpolation",
+  "EASING_TYPE": "Modus",
+  "LINEAR_EASING_TYPE": "Linear",
+  "IN_SINE_EASING_TYPE": "Nachgeben bei Sinus",
+  "OUT_SINE_EASING_TYPE": "Nachgeben von Sinus",
+  "IN_OUT_SINE_EASING_TYPE": "Leichtes Ein/aussteigen Sinus",
+  "IN_QUAD_EASING_TYPE": "Nachgeben bei Quadratisch",
+  "OUT_QUAD_EASING_TYPE": "Nachgeben von Quadratisch",
+  "IN_OUT_QUAD_EASING_TYPE": "Leichtes Ein/aussteigen Quadratisch",
+  "IN_CUBIC_EASING_TYPE": "Nachgeben bei Kubisch",
+  "OUT_CUBIC_EASING_TYPE": "Nachgeben von Kubisch",
+  "IN_OUT_CUBIC_EASING_TYPE": "Leichtes Ein/aussteigen Kubisch",
+  "IN_QUART_EASING_TYPE": "Nachgeben bei Quartisch",
+  "OUT_QUART_EASING_TYPE": "Nachgeben von Quartisch",
+  "IN_OUT_QUART_EASING_TYPE": "Leichtes Ein/aussteigen Quartisch",
+  "IN_QUINT_EASING_TYPE": "Nachgeben bei Quintisch",
+  "OUT_QUINT_EASING_TYPE": "Nachgeben on Quintisch",
+  "IN_OUT_QUINT_EASING_TYPE": "Leichtes Ein/aussteigen Quintisch",
+  "IN_EXPO_EASING_TYPE": "Nachgeben bei Exponential",
+  "OUT_EXPO_EASING_TYPE": "Nachgeben von Exponential",
+  "IN_OUT_EXPO_EASING_TYPE": "Leichtes Ein/aussteigen Exponential",
+  "IN_CIRC_EASING_TYPE": "Nachgeben bei Kreis",
+  "OUT_CIRC_EASING_TYPE": "Nachgeben von Kreis",
+  "IN_OUT_CIRC_EASING_TYPE": "Leichtes Ein/aussteigen Kreis",
+  "IN_BACK_EASING_TYPE": "Nachgeben bei Rückschwung",
+  "OUT_BACK_EASING_TYPE": "Nachgeben von Rückschwung",
+  "IN_OUT_BACK_EASING_TYPE": "Leichtes Ein/aussteigen über Rückschwung",
+  "IN_ELASTIC_EASING_TYPE": "Nachgeben bei Elastisch",
+  "OUT_ELASTIC_EASING_TYPE": "Nachgeben von Elastisch",
+  "IN_OUT_ELASTIC_EASING_TYPE": "Leichte Ein/aussteigen Elastisch",
+  "IN_BOUNCE_EASING_TYPE": "Nachgeben bei Sprung",
+  "OUT_BOUNCE_EASING_TYPE": "Nachgeben von Sprung",
+  "IN_OUT_BOUNCE_EASING_TYPE": "Leichte Ein/aussteigen Sprung",
+  "CLAMP_SHADER_TILE_NODE": "Begrenzen",
+  "REPEAT_SHADER_TILE_NODE": "Wiederholen",
+  "MIRROR_SHADER_TILE_NODE": "Spiegeln",
+  "DECAL_SHADER_TILE_NODE": "Abbilden",
+  "R_G_B_COMBINE_SEPARATE_COLOR_MODE": "RGB",
+  "H_S_V_COMBINE_SEPARATE_COLOR_MODE": "HSV",
+  "H_S_L_COMBINE_SEPARATE_COLOR_MODE": "HSL",
+  "COLOR_MANAGED_COLOR_SAMPLE_MODE": "Farbe gemanaged",
+  "RAW_COLOR_SAMPLE_MODE": "Rohfarbe",
+  "FRACTAL_PERLIN_NOISE_TYPE": "Perlin",
+  "TURBULENCE_PERLIN_NOISE_TYPE": "Turbulenz",
+  "INHERIT_COLOR_SPACE_TYPE": "Vererben",
+  "SRGB_COLOR_SPACE_TYPE": "sRGB",
+  "LINEAR_SRGB_COLOR_SPACE_TYPE": "Lineares sRGB",
+  "SIMPLE_OUTLINE_TYPE": "Einfach",
+  "GAUSSIAN_OUTLINE_TYPE": "Gaußsche",
+  "PIXEL_PERFECT_OUTLINE_TYPE": "Pixel Perfekt",
+  "DEGREES_ROTATION_TYPE": "Grad",
+  "RADIANS_ROTATION_TYPE": "Radiant",
+  "WEIGHTED_GRAYSCALE_MODE": "Gewichtet",
+  "AVERAGE_GRAYSCALE_MODE": "Durchschnitt",
+  "CUSTOM_GRAYSCALE_MODE": "Benutzerdefiniert",
+  "CLAMP_TILE_MODE": "Begrenzen",
+  "REPEAT_TILE_MODE": "Wiederholen",
+  "MIRROR_TILE_MODE": "Spiegeln",
+  "DECAL_TILE_MODE": "Abbilden",
+  "ERR_UNKNOWN_FILE_FORMAT": "Unbekanntes Dateiformat",
+  "ERR_UNKNOWN_IMG_FORMAT": "Unbekanntes Dateiformat '{0}'.",
+  "ERR_FAILED_GENERATE_SPRITE_SHEET": "Fehler beim erstellen des Sprite Sheets",
+  "ERR_NO_RENDERER": "Animations renderer nicht gefunden.",
+  "ERR_RENDERING_FAILED": "Rendern fehlgeschlagen",
+  "ENABLE_ANALYTICS": "Anonyme Daten sammeln",
+  "ANALYTICS_INFO": "Wir sammeln anonyme Nutzungsdaten um PixiEditor zu verbessern. Wir sammeln keine Persönlichen Daten.",
+  "LANGUAGE_INFO": "Alle Übersetzung sind durch unsere Community erstellt. Für mehr Informationen, trete unserem Discord bei.",
+  "UP_TO_DATE_UNKNOWN": "Konnte nicht nach Updates suchen",
+  "UP_TO_DATE": "PixiEditor Version ist aktuell",
+  "UPDATE_AVAILABLE": "Update {0} ist verfügbar",
+  "CHECKING_UPDATES": "Überprüfe auf Updates...",
+  "UPDATE_FAILED_DOWNLOAD": "Fehler beim Herunterladen des Updates",
+  "UPDATE_READY_TO_INSTALL": "Update ist bereit. Auf {0} wechseln?",
+  "SWITCH_TO_NEW_VERSION": "Wechseln",
+  "DOWNLOAD_UPDATE": "Herunterladen",
+  "CHECKING_FOR_UPDATES": "Überprüfe auf neue Updates...",
+  "PAINT_SHAPE_SETTING": "Pinselform",
+  "BOOL_OPERATION_NODE": "Boolische Operation",
+  "FIRST_SHAPE": "Erste Form",
+  "SECOND_SHAPE": "Zweite Form",
+  "OPERATION": "Operation",
+  "UNION_VECTOR_PATH_OP": "Vereinigung",
+  "DIFFERENCE_VECTOR_PATH_OP": "Differenz",
+  "INTERSECT_VECTOR_PATH_OP": "Schneidung",
+  "XOR_VECTOR_PATH_OP": "Exklusives Oder",
+  "NO_DOCUMENT_OPEN": "Hier ist nichts",
+  "EMPTY_DOCUMENT_ACTION_BTN": "Anfangen",
+  "ONBOARDING_TITLE": "Willkommen zu",
+  "ONBOARDING_DESCRIPTION": "Lass uns deinen Arbeitsbereich einrichten!",
+  "ONBOARDING_SKIP_BTN": "Überspringen",
+  "ONBOARDING_ACTION_BTN": "Anfangen",
+  "ONB_SELECT_PRIMARY_TOOLSET": "Wähle dein primäres Werkzeugset",
+  "ONB_NEXT_BTN": "Nächstes",
+  "ONB_BACK_BTN": "Vorheriges",
+  "ONB_ANALYTICS": "Anonymisierte Daten",
+  "ONB_ALL_SET": "Alles ist eingerichtet!",
+  "ONB_ALL_SET_BTN": "Anfangen",
+  "ANALYTICS_INFO_DETAILED": "PixiEditor sammelt anonyme Nutzungsdaten um die App zu verbessern. Die Daten enthalten keine Persönlichen Daten. Unter anderem sammelt PixiEditor:\n- Welche Werkzeuge wie verwendet werden\n- Wie lange du die App benutzt\n- Welche Kommandos du benutzt\n- Benchmarking Daten\n\nDu kannst dies jederzeit in den Einstellungen widerrufen.",
+  "PRIVACY_POLICY": "Datenschutzrichtlinien",
+  "ONB_SHORTCUTS": "Wähle deine Tastenbelegung",
+  "GRAPH_STATE_UNABLE_TO_CREATE_MEMBER": "Bei der aktuellen Knotenzusammenstellung kann keine Ebene neben der ausgewählten Ebene erstellt werden.",
+  "PRIMARY_TOOLSET": "Primäres Werkzeugset",
+  "OPEN_ONBOARDING_WINDOW": "Onboarding Fenster öffnen",
+  "AUTO_SCALE_BACKGROUND": "Hintergrund automatisch skalieren",
+  "UPDATES": "Updates",
+  "SCENE": "Szene",
+  "PRIMARY_BG_COLOR": "Primäre Hintergrund Farbe",
+  "SECONDARY_BG_COLOR": "Sekundäre Hintergrund Farbe",
+  "RESET": "Zurücksetzten",
+  "AUTOSAVE_OPEN_FOLDER": "Automatisch gespeicherte Dateien öffnen",
+  "AUTOSAVE_OPEN_FOLDER_DESCRIPTIVE": "Automatisch gespeicherte Dateien öffnen",
+  "AUTOSAVE_TOGGLE_DESCRIPTIVE": "Automatisches Speichern umschalten",
+  "OPEN_TYPE_FONT": "OpenType Schriftarten",
+  "TRUE_TYPE_FONT": "TrueType Schriftarten",
+  "SVG_FILE": "Skalierbare Vektor Grafik",
+  "EXAMPLE_FILES": "Beispieldatein",
+  "PROCEDURAL_GENERATION": "Prozedurale Animation",
+  "ERROR_GRAPH": "Knotenkonstellation hat einen Fehler produziert. Behebe ihn in der Knotenansicht",
+  "COLOR_MATRIX_FILTER_NODE": "Farbmatrix FIlter",
+  "WORKSPACE": "Arbeitsbereich",
+  "EXPORT_ZONE_NODE": "Zone exportieren",
+  "IS_DEFAULT_EXPORT": "Standard für Exportieren",
+  "EXPORT_OUTPUT": "Knotenergebnis",
+  "RENDER_OUTPUT_SIZE": "Exportierte Größe",
+  "RENDER_OUTPUT_CENTER": "Exportiertes Zentrum",
+  "COLOR_PICKER": "Pipette",
+  "UNAUTHORIZED_ACCESS": "Unautorisierter zugriff",
+  "SEPARATE_SHAPES": "Formen separieren",
+  "SEPARATE_SHAPES_DESCRIPTIVE": "Formen aus aktuellem Vektor in mehre Ebenen separieren",
+  "TEXT": "Text",
+  "EXTRACT_SELECTED_TEXT": "Ausgewählten Text extrahieren",
+  "EXTRACT_SELECTED_TEXT_DESCRIPTIVE": "Ausgewählten Text in neue Ebene extrahieren.",
+  "EXTRACT_SELECTED_CHARACTERS": "Ausgewählte Buchstaben extrahieren",
+  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Einzelne ausgewählte Buchstaben in neue Eben extrahieren.",
+  "STEP_START": "Zur nächstgelegenen vorherigen Zelle",
+  "STEP_END": "Zur nächstgelegenen nachfolgenden Zelle",
+  "STEP_FORWARD": "Nächster Frame",
+  "STEP_BACK": "Vorheriger Frame",
+  "ONB_FINISH_BTN": "Fertig",
+  "USER_NOT_FOUND": "Bitte gebe die E-Mail ein die du verwendet hast um die Founder's Edition zu kaufen.",
+  "SESSION_NOT_VALID": "Session ist nicht mehr valide, bitte logge dich erneut ein",
+  "SESSION_NOT_FOUND": "Session nicht gefunden, bitte logge dich erneut ein",
+  "INTERNAL_SERVER_ERROR": "Es gab einen internen Fehler des Servers. Bitte versuche es später erneut.",
+  "TOO_MANY_REQUESTS": "Zu viele Anfragen. Versuche es in {0} sekunden erneut.",
+  "SESSION_EXPIRED": "Session abgelaufen. bitte logge dich erneut ein.",
+  "CONNECTION_ERROR": "Verbindungsfehler. Bitte überprüfe deine Internetverbindung.",
+  "FAIL_LOAD_USER_DATA": "Fehler beim laden der Nutzerdaten",
+  "LOGOUT": "Ausloggen",
+  "LOGGED_IN_AS": "Hallo",
+  "EMAIL_SENT": "E-Mail gesendet! Schau in deinen Posteingang.",
+  "RESEND_ACTIVATION": "Erneut senden",
+  "INVALID_TOKEN": "Session ist invalide oder abgelaufen. Bitte versuche es erneut.",
+  "ENTER_EMAIL": "Gebe deine E-Mail ein",
+  "LOGIN_LINK": "Login Link senden",
+  "LOGIN_LINK_INFO": "Wir schicken dir einen sicheren Login Link per E-Mail. Kein Passwort ist erforderlich.",
+  "ACCOUNT_WINDOW_TITLE": "Account",
+  "CONNECTION_TIMEOUT": "Zeitüberschreitung bei der Verbindung. Bitte versuche es erneut.",
+  "OPEN_ACCOUNT_WINDOW": "Account verwalten",
+  "INSTALL": "Installieren",
+  "MANAGE_ACCOUNT": "Verwalten",
+  "OWNED_PRODUCTS": "Meine Inhalte",
+  "INSTALLING": "Installiere",
+  "INSTALLED": "Installiert",
+  "ACCOUNT_PROVIDER_INFO": "Nutzeraccount verwaltet von",
+  "UPDATE": "Update",
+  "FOUNDERS_BUNDLE": "Founder's Bundle",
+  "FOUNDERS_BUNDLE_SUBTEXT": "Unterstütze PixiEditor und verbessere deine Produktivität!",
+  "BECOME_A_FOUNDER": "Werde zum Founder",
+  "LOGIN": "Login",
+  "NOT_FOUNDER_YET": "Noch kein Founder?",
+  "ANIMATION_QUALITY_PRESET": "Qualität",
+  "VERY_LOW_QUALITY_PRESET": "Sehr niedrig",
+  "LOW_QUALITY_PRESET": "Niedrig",
+  "MEDIUM_QUALITY_PRESET": "Mittel",
+  "HIGH_QUALITY_PRESET": "Hoch",
+  "VERY_HIGH_QUALITY_PRESET": "Sehr hoch",
+  "EXPORT_FRAMES": "Frames exportieren",
+  "NORMALIZE_OFFSET": "Verschiebung normalisieren",
+  "TANGENT": "Tangente",
+  "EVALUATE_PATH_NODE": "Pfad evaluieren",
+  "OLD_MIN": "Alter Tiefstwert",
+  "OLD_MAX": "Alter Höchstwert",
+  "NEW_MIN": "Neuer Tiefstwert",
+  "NEW_MAX": "Neuer Höchstwert",
+  "REMAP_NODE": "Wertebereich umsetzten",
+  "TEXT_TOOL_ACTION_DISPLAY": "Klicke auf die Leinwand um Text hinzuzufügen (ziehe um die Größe festzulegen). Klicke auf existieren Text um ihn zu editieren.",
+  "PASTE_CELS": "Zellen einfügen",
+  "SCALE_X": "X Skalierung",
+  "SCALE_Y": "Y Skalierung",
+  "TRANSLATE_X": "X Verschiebung",
+  "TRANSLATE_Y": "Y Verschiebung",
+  "SKEW_X": "X Schiefe",
+  "SKEW_Y": "Y Schiefe",
+  "PERSPECTIVE_0": "Perspektive 1",
+  "PERSPECTIVE_1": "Perspektive 2",
+  "PERSPECTIVE_2": "Perspektive 3",
+  "COMPOSE_MATRIX": "Matrix komposieren",
+  "DECOMPOSE_MATRIX": "Matrix dekomposieren",
+  "NORMALIZE_COORDINATES": "Koordinaten normalisieren",
+  "TRANSFORMED_POSITION": "Transformierte Position",
+  "ACCOUNT_PROVIDER_NOT_AVAILABLE": "Dieser Version von PixiEditor unterstützt keine Nutzeraccounts. Bitte benutzte die offizielle Version von pixieditor.net um deinen Account zu verwalten."
 }

+ 10 - 29
src/PixiEditor/Data/Localization/Languages/en.json

@@ -56,11 +56,8 @@
   "INCREASE_TOOL_SIZE": "Increase tool size",
   "DECREASE_TOOL_SIZE": "Decrease tool size",
   "UPDATE_READY": "Update is ready to be installed. Do you want to install it now?",
-  "NEW_UPDATE": "New update",
   "COULD_NOT_UPDATE_WITHOUT_ADMIN": "Couldn't update without admin privileges. Please run PixiEditor as administrator.",
   "INSUFFICIENT_PERMISSIONS": "Insufficient permissions",
-  "UPDATE_CHECK_FAILED": "Update check failed",
-  "COULD_NOT_CHECK_FOR_UPDATES": "Could not check if there is an update available.",
   "VERSION": "Version {0}",
   "BUILD_ID": "Build ID: {0}",
   "OPEN_TEMP_DIR": "Open temp directory",
@@ -98,11 +95,8 @@
   "SHORTCUT_TEMPLATES": "Shortcut templates",
   "RESET_ALL": "Reset all",
   "LAYER": "Layer",
-  "LAYER_DELETE_SELECTED": "Delete active layer/folder",
-  "LAYER_DELETE_SELECTED_DESCRIPTIVE": "Delete active layer or folder",
   "LAYER_DELETE_ALL_SELECTED": "Delete all selected layers/folders",
   "LAYER_DELETE_ALL_SELECTED_DESCRIPTIVE": "Delete all selected layers and/or folders",
-  "DELETE_SELECTED_PIXELS": "Delete selected pixels",
   "NEW_FOLDER": "New folder",
   "CREATE_NEW_FOLDER": "Create new folder",
   "NEW_LAYER": "New layer",
@@ -179,7 +173,6 @@
   "COPY_COLOR_SECONDARY_RGB_DESCRIPTIVE": "Copy secondary color as RGB code",
   "PALETTE_COLORS": "Palette Colors",
   "REPLACE_SECONDARY_BY_PRIMARY": "Replace secondary color by primary",
-  "REPLACE_SECONDARY_BY_PRIMARY_DESCRIPTIVE": "Replace the secondary color by the primary color",
   "REPLACE_PRIMARY_BY_SECONDARY": "Replace primary color by secondary",
   "REPLACE_PRIMARY_BY_SECONDARY_DESCRIPTIVE": "Replace the primary color by the secondary color",
   "OPEN_PALETTE_BROWSER": "Open palette browser",
@@ -451,7 +444,6 @@
   "DELETE_PALETTE_CONFIRMATION": "Are you sure you want to delete this palette? This cannot be undone.",
   "SHORTCUTS_IMPORTED": "Shortcuts from {0} were imported successfully.",
   "SHORTCUT_PROVIDER_DETECTED": "We've detected, that you have {0} installed. Do you want to import shortcuts from it?",
-  "IMPORT_FROM_INSTALLATION": "Import from installation",
   "IMPORT_INSTALLATION_OPTION1": "Import from installation",
   "IMPORT_INSTALLATION_OPTION2": "Use defaults",
   "IMPORT_FROM_TEMPLATE": "Import from template",
@@ -474,7 +466,6 @@
   "SHORTCUT_ALREADY_ASSIGNED_OVERWRITE": "This shortcut is already assigned to '{0}'\nDo you want to replace the existing shortcut?",
   "UNSAVED_CHANGES": "Unsaved changes",
   "DOCUMENT_MODIFIED_SAVE": "The document has been modified. Do you want to save changes?",
-  "SESSION_UNSAVED_DATA": "{0} with unsaved data. Are you sure?",
   "PROJECT_MAINTAINERS": "Project Maintainers",
   "OTHER_AWESOME_CONTRIBUTORS": "And other awesome contributors",
   "HELP": "Help",
@@ -489,7 +480,6 @@
   "TOGGLE_VERTICAL_SYMMETRY": "Toggle vertical symmetry",
   "TOGGLE_HORIZONTAL_SYMMETRY": "Toggle horizontal symmetry",
   "RESET_VIEWPORT": "Reset viewport",
-  "VIEWPORT_SETTINGS": "Viewport settings",
   "MOVE_TOOL_ACTION_DISPLAY_TRANSFORMING": "Click and hold mouse to move pixels in selected layers.",
   "CTRL_KEY": "Ctrl",
   "SHIFT_KEY": "Shift",
@@ -514,7 +504,6 @@
   "SECURITY_ERROR_MSG": "No rights to write to the specified location.",
   "IO_ERROR": "IO error",
   "IO_ERROR_MSG": "Error while writing to disk.",
-  "FAILED_ASSOCIATE_PIXI": "Failed to associate .pixi file with PixiEditor.",
   "COULD_NOT_SAVE_PALETTE": "There was an error while saving the palette.",
   "NO_COLORS_TO_SAVE": "There are no colors to save.",
   "CANVAS": "Canvas",
@@ -555,7 +544,6 @@
   "COLOR_PICKER_ACTION_DISPLAY_REFERENCE_ONLY": "Click to pick colors from the reference layer.",
   "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Click to pick colors from the canvas.",
   "LOCALIZATION_DEBUG_WINDOW_TITLE": "Localization Debug Window",
-  "COMMAND_DEBUG_WINDOW_TITLE": "Command Debug Window",
   "SHORTCUTS_TITLE": "Shortcuts",
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_PERSPECTIVE": "Drag handles to scale transform. Hold Ctrl and drag a handle to scale from center. Hold Shift to scale proportionally. Hold Alt and drag a side handle to shear. Drag outside handles to rotate.",
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Drag handles to scale transform. Hold Ctrl and drag a handle to scale from center. Hold Shift to scale proportionally. Hold Alt and drag a side handle to shear. Drag outside handles to rotate.",
@@ -606,7 +594,6 @@
   "BACKGROUND": "Background",
   "OPACITY": "Opacity",
   "IS_VISIBLE": "Is visible",
-  "CLIP_TO_MEMBER_BELOW": "Clip to member below",
   "BLEND_MODE": "Blend mode",
   "MASK": "Mask",
   "MASK_IS_VISIBLE": "Mask is visible",
@@ -643,10 +630,13 @@
   "BIAS": "Bias",
   "TILE_MODE": "Tile Mode",
   "ON_ALPHA": "On Alpha",
-  "PIXEL_COORDINATE": "Pixel Coordinate",
   "OUTPUT_NODE": "Output",
   "NOISE_NODE": "Noise",
   "ELLIPSE_NODE": "Ellipse",
+  "LINE_NODE": "Line",
+  "LINE_START": "Start",
+  "LINE_END": "End",
+  "RECTANGLE_NODE": "Rectangle",
   "CREATE_IMAGE_NODE": "Create Image",
   "FOLDER_NODE": "Folder",
   "IMAGE_LAYER_NODE": "Image Layer",
@@ -656,7 +646,6 @@
   "MERGE_NODE": "Merge",
   "MODIFY_IMAGE_LEFT_NODE": "Begin Modify Image",
   "MODIFY_IMAGE_RIGHT_NODE": "End Modify Image",
-  "MODIFY_IMAGE_PAIR_NODE": "Modify Image",
   "COMBINE_CHANNELS_NODE": "Combine Channels",
   "COMBINE_COLOR_NODE": "Combine Color",
   "COMBINE_VECD_NODE": "Combine Vector",
@@ -683,7 +672,6 @@
   "OUTLINE_EXAMPLE": "Automatic Outline",
   "BETA_ANIMATIONS": "Animations",
   "SLIME_EXAMPLE": "Animated Slime",
-  "SHOW_ALL_EXAMPLES": "Show all",
   "APPLY_FILTER_NODE": "Apply Filter",
   "FILTER": "Filter",
   "LERP_NODE": "Lerp",
@@ -701,7 +689,6 @@
   "POINTS": "Points",
   "MIN_DISTANCE": "Min. Distance",
   "MAX_POINTS": "Max. Points",
-  "PROBABILITY": "Probability",
   "DISTRIBUTE_POINTS": "Distribute points",
   "REMOVE_CLOSE_POINTS": "Remove close points",
   "RASTERIZE_SHAPE": "Rasterize Shape",
@@ -875,7 +862,6 @@
   "INPUT_MATRIX": "Input Matrix",
   "OUTPUT_MATRIX": "Output Matrix",
   "CENTER": "Center",
-  "CONTENT_OFFSET": "Content Offset",
   "CANVAS_POSITION": "Canvas Position",
   "CENTER_POSITION": "Center Position",
   "TILE_MODE_X": "Tile Mode X",
@@ -884,7 +870,6 @@
   "SKEW": "Skew",
   "OFFSET_NODE": "Offset",
   "SKEW_NODE": "Skew",
-  "ROTATION_NODE": "Rotation",
   "SCALE_NODE": "Scale",
   "ROTATE_NODE": "Rotate",
   "TRANSFORM_NODE": "Transform",
@@ -908,8 +893,6 @@
   "CONTRAST_VALUE": "Contrast",
   "TEMPERATURE_VALUE": "Temperature",
   "TINT_VALUE": "Tint",
-  "FAILED_DOWNLOADING_UPDATE_TITLE": "Failed to download update",
-  "FAILED_DOWNLOADING_UPDATE": "Failed to download the update. Try again later.",
   "UNEXPECTED_SHUTDOWN": "Unexpected shutdown",
   "UNEXPECTED_SHUTDOWN_MSG": "PixiEditor was unexpectedly shut down. We've loaded latest autosave of your files.",
   "OK": "OK",
@@ -956,10 +939,6 @@
   "IN_BOUNCE_EASING_TYPE": "In Bounce",
   "OUT_BOUNCE_EASING_TYPE": "Out Bounce",
   "IN_OUT_BOUNCE_EASING_TYPE": "In Out Bounce",
-  "CLAMP_SHADER_TILE_NODE": "Clamp",
-  "REPEAT_SHADER_TILE_NODE": "Repeat",
-  "MIRROR_SHADER_TILE_NODE": "Mirror",
-  "DECAL_SHADER_TILE_NODE": "Decal",
   "R_G_B_COMBINE_SEPARATE_COLOR_MODE": "RGB",
   "H_S_V_COMBINE_SEPARATE_COLOR_MODE": "HSV",
   "H_S_L_COMBINE_SEPARATE_COLOR_MODE": "HSL",
@@ -994,7 +973,6 @@
   "UP_TO_DATE_UNKNOWN": "Couldn't check for updates",
   "UP_TO_DATE": "PixiEditor is up to date",
   "UPDATE_AVAILABLE": "Update {0} is available",
-  "CHECKING_UPDATES": "Checking for updates...",
   "UPDATE_FAILED_DOWNLOAD": "Failed to download the update",
   "UPDATE_READY_TO_INSTALL": "Update is ready. Switch to {0}?",
   "SWITCH_TO_NEW_VERSION": "Switch",
@@ -1057,7 +1035,6 @@
   "SECONDARY_BG_COLOR": "Secondary background color",
   "RESET": "Reset",
   "INSTALL": "Install",
-  "MANAGE_ACCOUNT": "Manage",
   "OWNED_PRODUCTS": "Owned Content",
   "INSTALLING": "Installing",
   "INSTALLED": "Installed",
@@ -1074,7 +1051,6 @@
   "ERROR_GRAPH": "Graph setup produced an error. Fix it in the node graph",
   "COLOR_MATRIX_FILTER_NODE": "Color Matrix Filter",
   "WORKSPACE": "Workspace",
-  "EXPORT_ZONE_NODE": "Export Zone",
   "IS_DEFAULT_EXPORT": "Is Default Export",
   "EXPORT_OUTPUT": "Export Output",
   "RENDER_OUTPUT_SIZE": "Render Output Size",
@@ -1122,5 +1098,10 @@
   "DECOMPOSE_MATRIX": "Decompose Matrix",
   "NORMALIZE_COORDINATES": "Normalize Coordinates",
   "TRANSFORMED_POSITION": "Transformed Position",
-  "ACCOUNT_PROVIDER_NOT_AVAILABLE": "This build of PixiEditor does not support accounts. Use the official build from pixieditor.net to manage your account."
+  "ACCOUNT_PROVIDER_NOT_AVAILABLE": "This build of PixiEditor does not support accounts. Use the official build from pixieditor.net to manage your account.",
+  "STEAM_OFFLINE": "Cannot validate the account. Steam is offline. Make sure Steam client is running and you are logged in.",
+  "ERROR_GPU_RESOURCES_CREATION": "Failed to create resources: Try updating your GPU drivers or try setting different rendering api in settings. \nError: '{0}'",
+  "ERROR_SAVING_PREFERENCES_DESC": "Failed to save preferences with error: '{0}'. Please check if you have write permissions to the PixiEditor data folder.",
+  "ERROR_SAVING_PREFERENCES": "Failed to save preferences",
+  "PREFERRED_RENDERER": "Preferred Render Api"
 }

+ 298 - 3
src/PixiEditor/Data/Localization/Languages/pl.json

@@ -667,8 +667,6 @@
   "MODIFY_IMAGE_PAIR_NODE": "Modyfikuj Obraz",
   "WITHOUT_FILTERS": "Bez filtrów",
   "RAW_LAYER_OUTPUT": "Warstwa",
-  "BETA_EXAMPLE_FILES": "Przykładowe pliki bety",
-  "BETA_PROCEDURAL_GENERATION": "Proceduralne Animacje",
   "POND_EXAMPLE": "Staw",
   "TREE_EXAMPLE": "Drzewo na Wietrze",
   "OUTLINE_EXAMPLE": "Automatyczna obwódka",
@@ -827,5 +825,302 @@
   "PRESERVE_ALPHA": "Zachowaj przezroczystość",
   "BLUR_FILTER_NODE": "Filtr rozmycia Gaussiana",
   "LENGTH": "Długość",
-  "GREATER_THAN_OR_EQUAL": "Większy lub równy"
+  "GREATER_THAN_OR_EQUAL": "Większy lub równy",
+  "WEBP_FILE": "Obrazy WebP",
+  "COLOR_NODE": "Kolor",
+  "CONVERT_TO_CURVE": "Konwertuj na ścieżkę",
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Konwertuj zaznaczoną warstwę wektorową na ścieżkę",
+  "FONT_FILES": "Pliki Fontów",
+  "UNIT_PT": "pt",
+  "FONT_LABEL": "Rodzina",
+  "FONT_SIZE_LABEL": "Rozmiar",
+  "SPACING_LABEL": "Odstęp",
+  "TEXT_TOOL": "Tekst",
+  "MISSING_FONT": "Brakujący font",
+  "TEXT_LAYER_NAME": "Tekst",
+  "TEXT_TOOL_TOOLTIP": "Stwórz tekst ({0}).",
+  "BOLD_TOOLTIP": "Pogrubienie",
+  "ITALIC_TOOLTIP": "Kursywa",
+  "CUSTOM_FONT": "Niestandardowy font",
+  "DUMP_GPU_DIAGNOSTICS": "Zapisz diagnostykę GPU",
+  "USE_SRGB_PROCESSING": "Użyj sRGB do przetwarzania kolorów",
+  "USE_SRGB_PROCESSING_DESC": "Konwertuj dokument używający liniowego sRGB na sRGB do przetwarzania kolorów. Wpływa na wygląd dokumentu.",
+  "TEXT_NODE": "Tekst",
+  "TEXT_LABEL": "Tekst",
+  "TEXT_ON_PATH_NODE": "Tekst na ścieżce",
+  "HIGH_DPI_RENDERING": "Renderuj w wysokim DPI",
+  "THICKNESS": "Grubość",
+  "TYPE": "Typ",
+  "EFFECTS": "Efekty",
+  "OUTLINE_NODE": "Obwódka",
+  "SHADER_CODE": "Kod Shadera",
+  "SHADER_NODE": "Shader",
+  "FAILED_TO_OPEN_EDITABLE_STRING_TITLE": "Nie udało się otworzyć pliku",
+  "FAILED_TO_OPEN_EDITABLE_STRING_MESSAGE": "Nie udało się edytować tego tekstu w zewnętrznym edytorze. Powód: {0}",
+  "STRING_EDIT_IN_DEFAULT_APP": "Edytuj w domyślnej aplikacji",
+  "STRING_OPEN_IN_FOLDER": "Otwórz w folderze",
+  "DISCO_BALL_EXAMPLE": "Kula Disco",
+  "COLOR_SPACE": "Przestrzeń Kolorów",
+  "PHOTO_EXAMPLES": "Zdjęcie",
+  "MASK_EXAMPLE": "Maska",
+  "SHADOW_NODE": "Filtr Cienia",
+  "INPUT_MATRIX": "Matryca Wejściowa",
+  "OUTPUT_MATRIX": "Matryca Wyjściowa",
+  "CENTER": "Środek",
+  "CONTENT_OFFSET": "Przesunięcie",
+  "CANVAS_POSITION": "Pozycja na płótnie",
+  "CENTER_POSITION": "Pozycja środka",
+  "TILE_MODE_X": "Tryb w osi X",
+  "TILE_MODE_Y": "Tryb w osi Y",
+  "TILE_NODE": "Kafelki",
+  "SKEW": "Przekrzywienie",
+  "OFFSET_NODE": "Przesunięcie",
+  "SKEW_NODE": "Przekrzywienie",
+  "ROTATION_NODE": "Rotacja",
+  "SCALE_NODE": "Skala",
+  "ROTATE_NODE": "Rotacja",
+  "TRANSFORM_NODE": "Przekształcenie",
+  "UNIT": "Jednostka",
+  "ANGLE": "Kąt",
+  "DOCUMENT_INFO_NODE": "Informacje Dokumentu",
+  "MASK_NODE": "Maska",
+  "SEPIA_FILTER_NODE": "Filtr Sepia",
+  "INTENSITY": "Intensywność",
+  "INVERT_FILTER_NODE": "Filtr Negatywu",
+  "COLOR_ADJUSTMENTS_FILTER": "Filtr Poprawy Kolorów",
+  "ADJUST_BRIGHTNESS": "Dostosuj Jasność",
+  "ADJUST_CONTRAST": "Dostosuj Kontrast",
+  "ADJUST_SATURATION": "Dostosuj Nasycenie",
+  "ADJUST_TEMPERATURE": "Dostosuj Temperaturę",
+  "ADJUST_TINT": "Dostosuj Tintę",
+  "ADJUST_HUE": "Dostosuj Odcień",
+  "HUE_VALUE": "Odcień",
+  "SATURATION_VALUE": "Nasycenie",
+  "BRIGHTNESS_VALUE": "Jasność",
+  "CONTRAST_VALUE": "Kontrast",
+  "TEMPERATURE_VALUE": "Temperatura",
+  "TINT_VALUE": "Tinta",
+  "FAILED_DOWNLOADING_UPDATE_TITLE": "Nie udało się pobrać aktualizacji",
+  "FAILED_DOWNLOADING_UPDATE": "Nie udało się pobrać aktualizacji. Spróbuj ponownie później.",
+  "DELETE_SELECTED": "Usuń zaznaczone",
+  "DELETE_SELECTED_DESCRIPTIVE": "Usuń zaznaczony element (warstwa, piksele, itp.)",
+  "GRIDLINES_SIZE": "Wielkość Siatki",
+  "CANVAS": "Płótno",
+  "UNEXPECTED_SHUTDOWN": "Niespodzewane wyłączenie",
+  "UNEXPECTED_SHUTDOWN_MSG": "A to pech! PixiEditor niespodziewanie zakończył pracę. Załadowaliśmy ostatnie auto-zapisy Twoich plików.",
+  "OK": "Ok",
+  "OPEN_AUTOSAVES": "Przeglądaj auto-zapisy",
+  "AUTOSAVE_SETTINGS_HEADER": "Automatyczne zapisy",
+  "AUTOSAVE_SETTINGS_SAVE_STATE": "Otwórz ostatnie pliki na starcie",
+  "AUTOSAVE_SETTINGS_PERIOD": "Interwał automatycznego zapisu",
+  "AUTOSAVE_ENABLED": "Autozapis włączony",
+  "MINUTE_UNIVERSAL": "min",
+  "AUTOSAVE_SETTINGS_SAVE_USER_FILE": "Automatycznie zapisz to oryginalnego pliku",
+  "LOAD_LAZY_FILE_MESSAGE": "Aby przyśpieszyć ładowanie, PixiEditor nie wczytał tego pliku. Kliknij przycisk aby go załadować.",
+  "EASING_NODE": "Łagodzenie",
+  "EASING_TYPE": "Typ Łagodzenia",
+  "OPEN_DIRECTORY_ON_EXPORT": "Otwórz folder zawierający plik przy eksporcie",
+  "ERROR_LOOP_DETECTED_MESSAGE": "Przesunięcie tej warstwy stworzy pętlę. Napraw to w edytorze Nodów.",
+  "LINEAR_EASING_TYPE": "Liniowy",
+  "IN_SINE_EASING_TYPE": "In Sine",
+  "OUT_SINE_EASING_TYPE": "Out Sine",
+  "IN_OUT_SINE_EASING_TYPE": "In Out Sine",
+  "IN_QUAD_EASING_TYPE": "In Quad",
+  "OUT_QUAD_EASING_TYPE": "Out Quad",
+  "IN_OUT_QUAD_EASING_TYPE": "In Out Quad",
+  "IN_CUBIC_EASING_TYPE": "In Cubic",
+  "OUT_CUBIC_EASING_TYPE": "Out Cubic",
+  "IN_OUT_CUBIC_EASING_TYPE": "In Out Cubic",
+  "IN_QUART_EASING_TYPE": "In Quart",
+  "OUT_QUART_EASING_TYPE": "Out Quart",
+  "IN_OUT_QUART_EASING_TYPE": "In Out Quart",
+  "IN_QUINT_EASING_TYPE": "In Quint",
+  "OUT_QUINT_EASING_TYPE": "Out Quint",
+  "IN_OUT_QUINT_EASING_TYPE": "In Out Quint",
+  "IN_EXPO_EASING_TYPE": "In Expo",
+  "OUT_EXPO_EASING_TYPE": "Out Expo",
+  "IN_OUT_EXPO_EASING_TYPE": "In Out Expo",
+  "IN_CIRC_EASING_TYPE": "In Circ",
+  "OUT_CIRC_EASING_TYPE": "Out Circ",
+  "IN_OUT_CIRC_EASING_TYPE": "In Out Circ",
+  "IN_BACK_EASING_TYPE": "In Back",
+  "OUT_BACK_EASING_TYPE": "Out Back",
+  "IN_OUT_BACK_EASING_TYPE": "In Out Back",
+  "IN_ELASTIC_EASING_TYPE": "In Elastic",
+  "OUT_ELASTIC_EASING_TYPE": "Out Elastic",
+  "IN_OUT_ELASTIC_EASING_TYPE": "In Out Elastic",
+  "IN_BOUNCE_EASING_TYPE": "In Bounce",
+  "OUT_BOUNCE_EASING_TYPE": "Out Bounce",
+  "IN_OUT_BOUNCE_EASING_TYPE": "In Out Bounce",
+  "CLAMP_SHADER_TILE_NODE": "Ogranicz",
+  "REPEAT_SHADER_TILE_NODE": "Powtórz",
+  "MIRROR_SHADER_TILE_NODE": "Lustrzane Odbicie",
+  "DECAL_SHADER_TILE_NODE": "Nakładka",
+  "R_G_B_COMBINE_SEPARATE_COLOR_MODE": "RGB",
+  "H_S_V_COMBINE_SEPARATE_COLOR_MODE": "HSV",
+  "H_S_L_COMBINE_SEPARATE_COLOR_MODE": "HSL",
+  "COLOR_MANAGED_COLOR_SAMPLE_MODE": "Zarządzany Kolorem",
+  "RAW_COLOR_SAMPLE_MODE": "Surowy",
+  "FRACTAL_PERLIN_NOISE_TYPE": "Perlin",
+  "TURBULENCE_PERLIN_NOISE_TYPE": "Turbulencja",
+  "INHERIT_COLOR_SPACE_TYPE": "Odziedzicz",
+  "SRGB_COLOR_SPACE_TYPE": "sRGB",
+  "LINEAR_SRGB_COLOR_SPACE_TYPE": "Liniowy sRGB",
+  "SIMPLE_OUTLINE_TYPE": "Prosty",
+  "GAUSSIAN_OUTLINE_TYPE": "Gaussowski",
+  "PIXEL_PERFECT_OUTLINE_TYPE": "Pixel Perfect",
+  "DEGREES_ROTATION_TYPE": "Stopnie",
+  "RADIANS_ROTATION_TYPE": "Radiany",
+  "WEIGHTED_GRAYSCALE_MODE": "Ważony",
+  "AVERAGE_GRAYSCALE_MODE": "Uśredniony",
+  "CUSTOM_GRAYSCALE_MODE": "Niestandardowy",
+  "CLAMP_TILE_MODE": "Ogranicz",
+  "REPEAT_TILE_MODE": "Powtórz",
+  "MIRROR_TILE_MODE": "Lustrzane Odbicie",
+  "DECAL_TILE_MODE": "Nakładka",
+  "ERR_UNKNOWN_FILE_FORMAT": "Nieznany format pliku",
+  "ERR_EXPORT_SIZE_INVALID": "Niepoprawna wielkość eksportu. Wartości muszą być większe niż 0.",
+  "ERR_UNKNOWN_IMG_FORMAT": "Nieznany format pliku '{0}'.",
+  "ERR_FAILED_GENERATE_SPRITE_SHEET": "Błąd przy generowaniu sprite sheetu",
+  "ERR_NO_RENDERER": "Renderer animacji nie znaleziony.",
+  "ERR_RENDERING_FAILED": "Błąd podczas renderowania",
+  "ENABLE_ANALYTICS": "Wysyłaj anonimową analitykę",
+  "ANALYTICS_INFO": "Zbieramy anonimową analitykę do usprawnienia PixiEditora. Nie zbieramy danych personalnych.",
+  "LANGUAGE_INFO": "Wszystkie tłumaczenia są zrobione przez społeczność. Dołącz do naszego serwera Discord po więcej informacji.",
+  "UP_TO_DATE_UNKNOWN": "Nie udało się sprawdzić aktualizacji",
+  "UP_TO_DATE": "Masz najnowszą wersję PixiEditora",
+  "UPDATE_AVAILABLE": "Aktualizacja {0} jest dostępna",
+  "CHECKING_UPDATES": "Sprawdzanie aktualizacji...",
+  "UPDATE_FAILED_DOWNLOAD": "Nie udało się pobrać aktualizacji",
+  "UPDATE_READY_TO_INSTALL": "Aktualizacja jest gotowa. Przełączyć na {0}?",
+  "SWITCH_TO_NEW_VERSION": "Przełącz",
+  "DOWNLOAD_UPDATE": "Pobierz",
+  "CHECKING_FOR_UPDATES": "Sprawdzanie aktualizacji...",
+  "PAINT_SHAPE_SETTING": "Kształt pędzla",
+  "BOOL_OPERATION_NODE": "Operacja Boolowska",
+  "FIRST_SHAPE": "Pierwszy kształt",
+  "SECOND_SHAPE": "Drugi kształt",
+  "OPERATION": "Operacja",
+  "UNION_VECTOR_PATH_OP": "Suma",
+  "DIFFERENCE_VECTOR_PATH_OP": "Różnica",
+  "INTERSECT_VECTOR_PATH_OP": "Część wspólna",
+  "XOR_VECTOR_PATH_OP": "XOR",
+  "REVERSE_DIFFERENCE_VECTOR_PATH_OP": "Odwrócona różnica",
+  "NO_DOCUMENT_OPEN": "Nic tu nie ma",
+  "EMPTY_DOCUMENT_ACTION_BTN": "Zacznij Tworzyć",
+  "ONBOARDING_TITLE": "Witaj w",
+  "ONBOARDING_DESCRIPTION": "Ustawmy twoją przestrzeń roboczą!",
+  "ONBOARDING_SKIP_BTN": "Pomiń",
+  "ONBOARDING_ACTION_BTN": "Zacznijmy",
+  "ONB_SELECT_PRIMARY_TOOLSET": "Wybierz swój domyślny zestaw narzędzi",
+  "ONB_NEXT_BTN": "Dalej",
+  "ONB_BACK_BTN": "Wróć",
+  "ONB_ANALYTICS": "Anonimowa analityka",
+  "ONB_ALL_SET": "Wszystko gotowe!",
+  "ONB_ALL_SET_BTN": "Zacznij tworzyć",
+  "ANALYTICS_INFO_DETAILED": "PixiEditor zbiera anonimową analitykę w celu poprawy działania aplikacji. Nie ma w niej żadnych danych personalnych. Między innymi, PixiEditor zbiera:\n- Jakie narzędzia używasz i jak,\n- Jak długo używasz aplikacji,\n- Jakie akcje wykonujesz,\n- Dane dot. wydajności\n\nMożesz w każdej chwili wyłączyć zbieranie analityki w ustawieniach",
+  "PRIVACY_POLICY": "Polityka Prywatności",
+  "ONB_SHORTCUTS": "Wybierz skróty klawiszowe",
+  "GRAPH_STATE_UNABLE_TO_CREATE_MEMBER": "Aktualny stan grafu nodów nie pozwala na tworzenie nowej warstwy obok aktualnie zaznaczonej.",
+  "PRIMARY_TOOLSET": "Domyślny zestaw narzędzi",
+  "OPEN_ONBOARDING_WINDOW": "Otwórz okno wdrożenia",
+  "AUTO_SCALE_BACKGROUND": "Automatycznie skaluj tło",
+  "UPDATES": "Aktualizacje",
+  "SCENE": "Scena",
+  "CUSTOM_BACKGROUND_SCALE": "Niestandardowa skala tła",
+  "PRIMARY_BG_COLOR": "Główny kolor tła",
+  "SECONDARY_BG_COLOR": "Drugorzędny kolor tła",
+  "RESET": "Resetuj",
+  "AUTOSAVE_OPEN_FOLDER": "Otwórz folder auto-zapisów",
+  "AUTOSAVE_OPEN_FOLDER_DESCRIPTIVE": "Otwórz folder, w którym auto-zapisy są przechowywane",
+  "AUTOSAVE_TOGGLE_DESCRIPTIVE": "Włącz/wyłącz auto-zapisy",
+  "OPEN_TYPE_FONT": "Fonty OpenType",
+  "TRUE_TYPE_FONT": "Fonty TrueType",
+  "SVG_FILE": "Skalowalne Grafiki Wektorowe",
+  "EXAMPLE_FILES": "Przykładowe Pliki",
+  "PROCEDURAL_GENERATION": "Proceduralna Animacja",
+  "ERROR_GRAPH": "Ustawienie grafu spowodowało błąd. Napraw to w edytorze nodów",
+  "COLOR_MATRIX_FILTER_NODE": "Filtr Matrycy Kolorów",
+  "WORKSPACE": "Obszar Roboczy",
+  "EXPORT_ZONE_NODE": "Strefa Eksportu",
+  "IS_DEFAULT_EXPORT": "Domyślny Eksport",
+  "EXPORT_OUTPUT": "Wyjście Eksportu",
+  "RENDER_OUTPUT_SIZE": "Rozmiar Renderowania",
+  "RENDER_OUTPUT_CENTER": "Środek Wyjścia Renderowania",
+  "COLOR_PICKER": "Próbnik Kolorów",
+  "UNAUTHORIZED_ACCESS": "Nieautoryzowany dostęp",
+  "SEPARATE_SHAPES": "Oddziel Kształty",
+  "SEPARATE_SHAPES_DESCRIPTIVE": "Oddziel kształty z aktualnej warstwy wektorowej do nowych warstw",
+  "TEXT": "Tekst",
+  "EXTRACT_SELECTED_TEXT": "Oddziel zaznaczony tekst",
+  "EXTRACT_SELECTED_TEXT_DESCRIPTIVE": "Oddziel zaznaczony tekst do nowej warstwy.",
+  "EXTRACT_SELECTED_CHARACTERS": "Oddziel zaznaczone znaki",
+  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Oddziel pojedyncze znaki z zaznaczenia do nowych warstw.",
+  "STEP_START": "Cofnij do najbliższej celi",
+  "STEP_END": "Przejdź do najbliższej celi",
+  "STEP_FORWARD": "Przejdź klatkę do przodu",
+  "STEP_BACK": "Przejdź klatkę do tyłu",
+  "ONB_FINISH_BTN": "Zakończ",
+  "USER_NOT_FOUND": "Wprowadź email, którego użyłeś do zakupu Edycji Fundatorskiej.",
+  "SESSION_NOT_VALID": "Sesja nie jest poprawna. Zaloguj się ponownie",
+  "SESSION_NOT_FOUND": "Nie znaleziono sesji, zaloguj się ponownie",
+  "INTERNAL_SERVER_ERROR": "Wystąpił błąd po naszej stronie. Spróbuj ponownie później.",
+  "TOO_MANY_REQUESTS": "Za dużo zapytań. Spróbuj ponownie za {0} sekund.",
+  "SESSION_EXPIRED": "Sesja wygasła. Zaloguj się ponownie.",
+  "CONNECTION_ERROR": "Błąd połączenia. Sprawdź swoje połączenie z internetem.",
+  "FAIL_LOAD_USER_DATA": "Nie udało się wczytać danych użytkownika",
+  "LOGOUT": "Wyloguj",
+  "LOGGED_IN_AS": "Cześć",
+  "EMAIL_SENT": "Poszło! Sprawdź swoją skrzynkę email.",
+  "RESEND_ACTIVATION": "Wyślij ponownie",
+  "INVALID_TOKEN": "Sesja wygasła lub jest niepoprawna. Zaloguj się ponownie.",
+  "ENTER_EMAIL": "Wpisz swój email",
+  "LOGIN_LINK": "Wyślij link do logowania",
+  "LOGIN_LINK_INFO": "Nie potrzeba hasła. Wyślemy Ci emailem bezpieczny link do logowania.",
+  "ACCOUNT_WINDOW_TITLE": "Konto",
+  "CONNECTION_TIMEOUT": "Upłynął limit czasu połączenia. Spróbuj ponownie.",
+  "OPEN_ACCOUNT_WINDOW": "Zarządzaj Kontem",
+  "INSTALL": "Instaluj",
+  "MANAGE_ACCOUNT": "Zarządzaj",
+  "OWNED_PRODUCTS": "Posiadana Zawartość",
+  "INSTALLING": "Instalowanie",
+  "INSTALLED": "Zainstalowano",
+  "ACCOUNT_PROVIDER_INFO": "Konto zarządzane przez",
+  "UPDATE": "Aktualizuj",
+  "FOUNDERS_BUNDLE": "Pakiet Fundatorski",
+  "FOUNDERS_BUNDLE_SUBTEXT": "Wspieraj PixiEditora i przyśpiesz swoją pracę!",
+  "BECOME_A_FOUNDER": "Zostań Fundatorem",
+  "LOGIN": "Zaloguj",
+  "NOT_FOUNDER_YET": "Nie jesteś jeszcze Fundatorem?",
+  "ANIMATION_QUALITY_PRESET": "Jakość",
+  "VERY_LOW_QUALITY_PRESET": "Bardzo niska",
+  "LOW_QUALITY_PRESET": "Niska",
+  "MEDIUM_QUALITY_PRESET": "Średnia",
+  "HIGH_QUALITY_PRESET": "Wysoka",
+  "VERY_HIGH_QUALITY_PRESET": "Bardzo Wysoka",
+  "EXPORT_FRAMES": "Eksportuj Klatki",
+  "NORMALIZE_OFFSET": "Normalizuj przesunięcie",
+  "TANGENT": "Tangens",
+  "EVALUATE_PATH_NODE": "Oblicz Ścieżkę",
+  "OLD_MIN": "Stare Minimum",
+  "OLD_MAX": "Stare Maksimum",
+  "NEW_MIN": "Nowe Minimum",
+  "NEW_MAX": "Nowe Maksimum",
+  "REMAP_NODE": "Przekształć zakres",
+  "TEXT_TOOL_ACTION_DISPLAY": "Kliknij na płótno aby dodać nowy tekst (przeciągnij podczas przyciśnięcia aby ustawić wielkość). Kliknij na istniejący tekst aby go edytować.",
+  "PASTE_CELS": "Wklej klatki",
+  "SCALE_X": "Skala X",
+  "SCALE_Y": "Skala Y",
+  "TRANSLATE_X": "Przesunięcie X",
+  "TRANSLATE_Y": "Przesunięcie Y",
+  "SKEW_X": "Przekrzywienie X",
+  "SKEW_Y": "Przekrzywienie Y",
+  "PERSPECTIVE_0": "Perspektywa 0",
+  "PERSPECTIVE_1": "Perspektywa 1",
+  "PERSPECTIVE_2": "Perspektywa 2",
+  "COMPOSE_MATRIX": "Złóż Matrycę",
+  "DECOMPOSE_MATRIX": "Rozłóż Matrycę",
+  "NORMALIZE_COORDINATES": "Normalizuj współrzędne",
+  "TRANSFORMED_POSITION": "Obliczona Pozycja",
+  "ACCOUNT_PROVIDER_NOT_AVAILABLE": "Ta wersja PixiEditora nie wspiera zarządzania kontami. Użyj oficjalnej wersji pobranej z pixieditor.net."
 }

+ 8 - 3
src/PixiEditor/Data/Localization/Languages/ru.json

@@ -654,8 +654,6 @@
   "BUILD_ID": "ID сборки: {0}",
   "WITHOUT_FILTERS": "Без фильтров",
   "RAW_LAYER_OUTPUT": "Исходник",
-  "BETA_EXAMPLE_FILES": "Примеры (Beta)",
-  "BETA_PROCEDURAL_GENERATION": "Процедурная анимация",
   "POND_EXAMPLE": "Пруд",
   "TREE_EXAMPLE": "Ветреный день",
   "OUTLINE_EXAMPLE": "Автообводка",
@@ -673,5 +671,12 @@
   "NEW_PALETTE_FILE": "палитра",
   "CHANGE_ACTIVE_FRAME_PREVIOUS": "Перейти на предыдущий кадр",
   "CHANGE_ACTIVE_FRAME_NEXT": "Перейти на следующий кадр",
-  "NEW_FROM_CLIPBOARD": "Создать из буфера обмена"
+  "NEW_FROM_CLIPBOARD": "Создать из буфера обмена",
+  "MIN_DISTANCE": "Мин. расстояние",
+  "MAX_POINTS": "Макс. точек",
+  "DISTRIBUTE_POINTS": "Распределить точки",
+  "REMOVE_CLOSE_POINTS": "Удалить близкие точки",
+  "RASTERIZE_SHAPE": "Растрировать фигуру",
+  "MODE": "Режим",
+  "Factor": "Степень"
 }

+ 1130 - 0
src/PixiEditor/Data/Localization/Languages/tr.json

@@ -0,0 +1,1130 @@
+{
+  "RECENT_FILES": "Son Dosyalar",
+  "OPEN_FILE": "Dosya Aç",
+  "NEW_FILE": "Yeni",
+  "RECENT_EMPTY_TEXT": "Çok fazla boş alan",
+  "LANGUAGE": "Dil",
+  "GENERAL": "Genel",
+  "DISCORD": "Discord",
+  "KEY_BINDINGS": "Klavye Kısayolları",
+  "MISC": "Çeşitli",
+  "SHOW_STARTUP_WINDOW": "Başlangıç Penceresini Göster",
+  "RECENT_FILE_LENGTH": "Son dosyalar listesi uzunluğu",
+  "RECENT_FILE_LENGTH_TOOLTIP": "Dosya > Son Kullanılanlar altında kaç belgenin gösterileceği. Varsayılan: 8",
+  "DEFAULT_NEW_SIZE": "Varsayılan yeni dosya boyutu",
+  "WIDTH": "Genişlik",
+  "HEIGHT": "Yükseklik",
+  "TOOLS": "Araçlar",
+  "ENABLE_SHARED_TOOLBAR": "Paylaşılan araç çubuğunu etkinleştir",
+  "AUTOMATIC_UPDATES": "Otomatik Güncellemeler",
+  "CHECK_FOR_UPDATES": "Başlangıçta güncellemeleri kontrol et",
+  "UPDATE_STREAM": "Güncelleme akışı",
+  "UPDATE_CHANNEL_HELP_TOOLTIP": "Güncelleme kanalları yalnızca bağımsız sürümde değiştirilebilir (https://pixieditor.net adresinden indirilir).\nSteam ve Microsoft Store sürümleri güncellemeleri ayrı olarak yönetir.",
+  "DEBUG": "Hata Ayıklama",
+  "ENABLE_DEBUG_MODE": "Hata ayıklama modunu etkinleştir",
+  "OPEN_CRASH_REPORTS_DIR": "Kilitlenme raporları dizinini aç",
+  "DISCORD_RICH_PRESENCE": "Rich Presence",
+  "ENABLED": "Etkin",
+  "SHOW_IMAGE_NAME": "Resim adını göster",
+  "SHOW_IMAGE_SIZE": "Resim boyutunu göster",
+  "SHOW_LAYER_COUNT": "Katman sayısını göster",
+  "FILE": "Dosya",
+  "RECENT": "Son Kullanılanlar",
+  "OPEN": "Aç",
+  "SAVE_PIXI": "Kaydet (.pixi)",
+  "SAVE_AS_PIXI": "Farklı Kaydet... (.pixi)",
+  "EXPORT_IMG": "Dışa Aktar (.png, .jpg, vb.)",
+  "EDIT": "Düzenle",
+  "EXIT": "Çıkış",
+  "PERCENTAGE": "Yüzde",
+  "ABSOLUTE": "Mutlak",
+  "PRESERVE_ASPECT_RATIO": "En boy oranını koru",
+  "ANCHOR_POINT": "Bağlantı noktası",
+  "RESIZE_IMAGE": "Resmi yeniden boyutlandır",
+  "RESIZE": "Yeniden Boyutlandır",
+  "DOCUMENTATION": "Belgelendirme",
+  "WEBSITE": "Web Sitesi",
+  "OPEN_WEBSITE": "Web sitesini aç",
+  "REPOSITORY": "Depo",
+  "OPEN_REPOSITORY": "Depoyu aç",
+  "OPEN_DOCUMENTATION": "Belgelendirmeyi aç",
+  "LICENSE": "Lisans",
+  "OPEN_LICENSE": "Lisansı aç",
+  "THIRD_PARTY_LICENSES": "Üçüncü taraf lisansları",
+  "OPEN_THIRD_PARTY_LICENSES": "Üçüncü taraf lisanslarını aç",
+  "APPLY_TRANSFORM": "Dönüşümü uygula",
+  "INCREASE_TOOL_SIZE": "Araç boyutunu artır",
+  "DECREASE_TOOL_SIZE": "Araç boyutunu azalt",
+  "UPDATE_READY": "Güncelleme yüklenmeye hazır. Şimdi yüklemek ister misiniz?",
+  "NEW_UPDATE": "Yeni güncelleme",
+  "COULD_NOT_UPDATE_WITHOUT_ADMIN": "Yönetici ayrıcalıkları olmadan güncelleme yapılamadı. Lütfen PixiEditor'ı yönetici olarak çalıştırın.",
+  "INSUFFICIENT_PERMISSIONS": "Yetersiz izinler",
+  "UPDATE_CHECK_FAILED": "Güncelleme kontrolü başarısız oldu",
+  "COULD_NOT_CHECK_FOR_UPDATES": "Yeni bir güncelleme olup olmadığı kontrol edilemedi.",
+  "VERSION": "Sürüm {0}",
+  "BUILD_ID": "Yapı ID: {0}",
+  "OPEN_TEMP_DIR": "Temp dizini aç",
+  "OPEN_LOCAL_APPDATA_DIR": "Local AppData dizinini aç",
+  "OPEN_ROAMING_APPDATA_DIR": "Roaming AppData dizinini aç",
+  "OPEN_INSTALLATION_DIR": "Kurulum dizinini aç",
+  "DUMP_ALL_COMMANDS": "Tüm komutları dök",
+  "DUMP_ALL_COMMANDS_DESCRIPTIVE": "Tüm komutları bir metin dosyasına dök",
+  "CRASH": "Çökme",
+  "CRASH_APP": "Uygulamayı çökert",
+  "DELETE_USR_PREFS": "Kullanıcı tercihlerini sil (Roaming AppData)",
+  "DELETE_SHORTCUT_FILE": "Kısayol dosyasını sil (Roaming AppData)",
+  "DELETE_EDITOR_DATA": "Editör verilerini sil (Local AppData)",
+  "GENERATE_KEY_BINDINGS_TEMPLATE": "Klavye kısayolları şablonu oluştur",
+  "GENERATE_KEY_BINDINGS_TEMPLATE_DESCRIPTIVE": "Klavye kısayolları json şablonu oluştur",
+  "VALIDATE_SHORTCUT_MAP": "Kısayol haritasını doğrula",
+  "VALIDATE_SHORTCUT_MAP_DESCRIPTIVE": "Kısayol haritasını doğrular",
+  "VALIDATION_KEYS_NOTICE_DIALOG": "Boş anahtarlar: {0}\nBilinmeyen Komutlar: {1}",
+  "RESULT": "Sonuç",
+  "CLEAR_RECENT_DOCUMENTS": "Son belgeleri temizle",
+  "CLEAR_RECENTLY_OPENED_DOCUMENTS": "Son açılan belgeleri temizle",
+  "OPEN_CMD_DEBUG_WINDOW": "Komut hata ayıklama penceresini aç",
+  "PATH_DOES_NOT_EXIST": "{0} mevcut değil.",
+  "LOCATION_DOES_NOT_EXIST": "Konum mevcut değil.",
+  "FILE_NOT_FOUND": "Dosya bulunamadı.",
+  "ARE_YOU_SURE": "Emin misiniz?",
+  "ARE_YOU_SURE_PATH_FULL_PATH": "{0} dosyasını silmek istediğinizden emin misiniz?\nBu veriler tüm kurulumlar için kaybolacaktır.\n(Tam Yol: {1})",
+  "FAILED_TO_OPEN_FILE": "Dosya açılamadı",
+  "OLD_FILE_FORMAT": "Eski dosya biçimi",
+  "OLD_FILE_FORMAT_DESCRIPTION": "Bu .pixi dosyası artık desteklenmeyen\nve açılamayan eski biçimi kullanıyor.",
+  "NOTHING_FOUND": "Hiçbir şey bulunamadı",
+  "EXPORT": "Dışa Aktar",
+  "EXPORT_IMAGE": "Resmi dışa aktar",
+  "IMPORT": "İçe Aktar",
+  "SHORTCUT_TEMPLATES": "Kısayol şablonları",
+  "RESET_ALL": "Tümünü sıfırla",
+  "LAYER": "Katman",
+  "LAYER_DELETE_SELECTED": "Aktif katmanı/klasörü sil",
+  "LAYER_DELETE_SELECTED_DESCRIPTIVE": "Aktif katmanı veya klasörü sil",
+  "LAYER_DELETE_ALL_SELECTED": "Seçili tüm katmanları/klasörleri sil",
+  "LAYER_DELETE_ALL_SELECTED_DESCRIPTIVE": "Seçili tüm katmanları ve/veya klasörleri sil",
+  "DELETE_SELECTED_PIXELS": "Seçili pikselleri sil",
+  "NEW_FOLDER": "Yeni klasör",
+  "CREATE_NEW_FOLDER": "Yeni klasör oluştur",
+  "NEW_LAYER": "Yeni katman",
+  "CREATE_NEW_LAYER": "Yeni katman oluştur",
+  "NEW_IMAGE": "Yeni resim",
+  "CREATE_NEW_IMAGE": "Yeni resim oluştur",
+  "SAVE": "Kaydet",
+  "SAVE_AS": "Farklı kaydet...",
+  "IMAGE": "Resim",
+  "SAVE_IMAGE": "Resmi kaydet",
+  "SAVE_IMAGE_AS": "Resmi yeni olarak kaydet",
+  "DUPLICATE": "Çoğalt",
+  "DUPLICATE_SELECTED_LAYER": "Seçili katmanı çoğalt",
+  "CREATE_MASK": "Maske oluştur",
+  "DELETE_MASK": "Maskeyi sil",
+  "TOGGLE_MASK": "Maskeyi aç/kapat",
+  "APPLY_MASK": "Maskeyi uygula",
+  "TOGGLE_VISIBILITY": "Görünürlüğü aç/kapat",
+  "MOVE_MEMBER_UP": "Öğeyi yukarı taşı",
+  "MOVE_MEMBER_UP_DESCRIPTIVE": "Seçili katmanı veya klasörü yukarı taşı",
+  "MOVE_MEMBER_DOWN": "Öğeyi aşağı taşı",
+  "MOVE_MEMBER_DOWN_DESCRIPTIVE": "Seçili katmanı veya klasörü aşağı taşı",
+  "MERGE_ALL_SELECTED_LAYERS": "Seçili tüm katmanları birleştir",
+  "MERGE_WITH_ABOVE": "Seçili katmanı üsttekiyle birleştir",
+  "MERGE_WITH_ABOVE_DESCRIPTIVE": "Seçili katmanı üstündekiyle birleştir",
+  "MERGE_WITH_BELOW": "Seçili katmanı alttakiyle birleştir",
+  "MERGE_WITH_BELOW_DESCRIPTIVE": "Seçili katmanı altındakiyle birleştir",
+  "ADD_REFERENCE_LAYER": "Referans Katmanı Ekle",
+  "DELETE_REFERENCE_LAYER": "Referans katmanını sil",
+  "TRANSFORM_REFERENCE_LAYER": "Referans katmanını dönüştür",
+  "TOGGLE_REFERENCE_LAYER_POS": "Referans katmanı konumunu değiştir",
+  "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Referans katmanını en üst veya en alt arasında değiştir",
+  "RESET_REFERENCE_LAYER_POS": "Referans katmanı konumunu sıfırla",
+  "CLIP_CANVAS": "Tuvali Kırp",
+  "FLIP_IMG_VERTICALLY": "Resmi Dikey Çevir",
+  "FLIP_IMG_HORIZONTALLY": "Resmi Yatay Çevir",
+  "FLIP_LAYERS_VERTICALLY": "Seçili Katmanları Dikey Çevir",
+  "FLIP_LAYERS_HORIZONTALLY": "Seçili Katmanları Yatay Çevir",
+  "ROT_IMG_90": "Resmi 90 derece döndür",
+  "ROT_IMG_180": "Resmi 180 derece döndür",
+  "ROT_IMG_-90": "Resmi -90 derece döndür",
+  "ROT_LAYERS_90": "Seçili Katmanları 90 derece döndür",
+  "ROT_LAYERS_180": "Seçili Katmanları 180 derece döndür",
+  "ROT_LAYERS_-90": "Seçili Katmanları -90 derece döndür",
+  "TOGGLE_VERT_SYMMETRY_AXIS": "Dikey simetri eksenini aç/kapat",
+  "TOGGLE_HOR_SYMMETRY_AXIS": "Yatay simetri eksenini aç/kapat",
+  "DELETE_SELECTED": "Seçili olanı sil",
+  "DELETE_SELECTED_DESCRIPTIVE": "Seçili öğeyi sil (katman, pikseller, vb.)",
+  "RESIZE_DOCUMENT": "Belgeyi yeniden boyutlandır",
+  "RESIZE_CANVAS": "Tuvali yeniden boyutlandır",
+  "CENTER_CONTENT": "İçeriği ortala",
+  "CUT": "Kes",
+  "CUT_DESCRIPTIVE": "Seçili alanı/katmanları kes",
+  "PASTE": "Yapıştır",
+  "PASTE_DESCRIPTIVE": "Pano içeriğini yapıştır",
+  "PASTE_AS_NEW_LAYER": "Yeni katman olarak yapıştır",
+  "PASTE_AS_NEW_LAYER_DESCRIPTIVE": "Panodan yeni katman olarak yapıştır",
+  "PASTE_REFERENCE_LAYER": "Referans katmanı yapıştır",
+  "PASTE_REFERENCE_LAYER_DESCRIPTIVE": "Pano içeriğini referans katmanı olarak yapıştır",
+  "PASTE_COLOR": "Rengi yapıştır",
+  "PASTE_COLOR_DESCRIPTIVE": "Panodan rengi yapıştır",
+  "PASTE_COLOR_SECONDARY": "Rengi ikincil olarak yapıştır",
+  "PASTE_COLOR_SECONDARY_DESCRIPTIVE": "Panodan rengi ikincil renk olarak yapıştır",
+  "CLIPBOARD": "Pano",
+  "COPY": "Kopyala",
+  "COPY_DESCRIPTIVE": "Panoya kopyala",
+  "COPY_COLOR_HEX": "Birincil rengi kopyala (HEX)",
+  "COPY_COLOR_HEX_DESCRIPTIVE": "Birincil rengi HEX kodu olarak kopyala",
+  "COPY_COLOR_RGB": "Birincil rengi kopyala (RGB)",
+  "COPY_COLOR_RGB_DESCRIPTIVE": "Birincil rengi RGB kodu olarak kopyala",
+  "COPY_COLOR_SECONDARY_HEX": "İkincil rengi kopyala (HEX)",
+  "COPY_COLOR_SECONDARY_HEX_DESCRIPTIVE": "İkincil rengi HEX kodu olarak kopyala",
+  "COPY_COLOR_SECONDARY_RGB": "İkincil rengi kopyala (RGB)",
+  "COPY_COLOR_SECONDARY_RGB_DESCRIPTIVE": "İkincil rengi RGB kodu olarak kopyala",
+  "PALETTE_COLORS": "Palet Renkleri",
+  "REPLACE_SECONDARY_BY_PRIMARY": "İkincil rengi birincil ile değiştir",
+  "REPLACE_SECONDARY_BY_PRIMARY_DESCRIPTIVE": "İkincil rengi birincil renk ile değiştir",
+  "REPLACE_PRIMARY_BY_SECONDARY": "Birincil rengi ikincil ile değiştir",
+  "REPLACE_PRIMARY_BY_SECONDARY_DESCRIPTIVE": "Birincil rengi ikincil renk ile değiştir",
+  "OPEN_PALETTE_BROWSER": "Palet tarayıcısını aç",
+  "OVERWRITE_PALETTE_CONSENT": "'{0}' paleti zaten mevcut, üzerine yazmak istiyor musunuz?",
+  "PALETTE_EXISTS": "Palet zaten mevcut",
+  "REPLACE_PALETTE_CONSENT": "Mevcut paleti seçilenle değiştirmek istiyor musunuz?",
+  "REPLACE_PALETTE": "Mevcut paleti değiştir",
+  "SELECT_COLOR_1": "Renk 1'i seç",
+  "SELECT_COLOR_2": "Renk 2'yi seç",
+  "SELECT_COLOR_3": "Renk 3'ü seç",
+  "SELECT_COLOR_4": "Renk 4'ü seç",
+  "SELECT_COLOR_5": "Renk 5'i seç",
+  "SELECT_COLOR_6": "Renk 6'yı seç",
+  "SELECT_COLOR_7": "Renk 7'yi seç",
+  "SELECT_COLOR_8": "Renk 8'i seç",
+  "SELECT_COLOR_9": "Renk 9'u seç",
+  "SELECT_COLOR_10": "Renk 10'u seç",
+  "SELECT_TOOL": "{0} Aracını Seç",
+  "SELECT_COLOR_1_DESCRIPTIVE": "Paletteki ilk rengi seç",
+  "SELECT_COLOR_2_DESCRIPTIVE": "Paletteki ikinci rengi seç",
+  "SELECT_COLOR_3_DESCRIPTIVE": "Paletteki üçüncü rengi seç",
+  "SELECT_COLOR_4_DESCRIPTIVE": "Paletteki dördüncü rengi seç",
+  "SELECT_COLOR_5_DESCRIPTIVE": "Paletteki beşinci rengi seç",
+  "SELECT_COLOR_6_DESCRIPTIVE": "Paletteki altıncı rengi seç",
+  "SELECT_COLOR_7_DESCRIPTIVE": "Paletteki yedinci rengi seç",
+  "SELECT_COLOR_8_DESCRIPTIVE": "Paletteki sekizinci rengi seç",
+  "SELECT_COLOR_9_DESCRIPTIVE": "Paletteki dokuzuncu rengi seç",
+  "SELECT_COLOR_10_DESCRIPTIVE": "Paletteki onuncu rengi seç",
+  "SWAP_COLORS": "Renkleri değiştir",
+  "SWAP_COLORS_DESCRIPTIVE": "Birincil ve ikincil renkleri değiştir",
+  "SEARCH": "Ara",
+  "COMMAND_SEARCH": "Komut arama",
+  "OPEN_COMMAND_SEARCH": "Komut arama penceresini aç",
+  "SELECT": "Seç",
+  "DESELECT": "Seçimi Kaldır",
+  "INVERT": "Ters Çevir",
+  "SELECTION": "Seçim",
+  "SELECT_ALL": "Tümünü seç",
+  "SELECT_ALL_DESCRIPTIVE": "Her şeyi seç",
+  "CLEAR_SELECTION": "Seçimi temizle",
+  "INVERT_SELECTION": "Seçimi ters çevir",
+  "INVERT_SELECTION_DESCRIPTIVE": "Seçimi ters çevir",
+  "TRANSFORM_SELECTED_AREA": "Seçili alanı dönüştür",
+  "NUDGE_SELECTED_LEFT": "Seçili nesneyi sola kaydır",
+  "NUDGE_SELECTED_RIGHT": "Seçili nesneyi sağa kaydır",
+  "NUDGE_SELECTED_UP": "Seçili nesneyi yukarı kaydır",
+  "NUDGE_SELECTED_DOWN": "Seçili nesneyi aşağı kaydır",
+  "MASK_FROM_SELECTION": "Seçimden yeni maske",
+  "MASK_FROM_SELECTION_DESCRIPTIVE": "Seçimi yeni maskeye dönüştür",
+  "ADD_SELECTION_TO_MASK": "Seçimi maskeye ekle",
+  "SUBTRACT_SELECTION_FROM_MASK": "Seçimi maskeden çıkar",
+  "INTERSECT_SELECTION_MASK": "Seçimi maskeyle kesiştir",
+  "SELECTION_TO_MASK": "Seçimi maskeye",
+  "TO_NEW_MASK": "yeni maskeye",
+  "ADD_TO_MASK": "maskeye ekle",
+  "SUBTRACT_FROM_MASK": "maskeden çıkar",
+  "INTERSECT_WITH_MASK": "maskeyle kesiştir",
+  "STYLUS": "Kalem",
+  "TOGGLE_PEN_MODE": "Kalem modunu aç/kapat",
+  "UNDO": "Geri Al",
+  "UNDO_DESCRIPTIVE": "Son eylemi geri al",
+  "REDO": "Yinele",
+  "REDO_DESCRIPTIVE": "Son eylemi yinele",
+  "WINDOWS": "Pencereler",
+  "TOGGLE_GRIDLINES": "Kılavuz çizgilerini aç/kapat",
+  "GRIDLINES_SIZE": "Kılavuz Boyutu",
+  "ZOOM_IN": "Yakınlaştır",
+  "ZOOM_OUT": "Uzaklaştır",
+  "NEW_WINDOW_FOR_IMG": "Mevcut resim için yeni pencere",
+  "CENTER_ACTIVE_VIEWPORT": "Aktif görünüm alanını ortala",
+  "FLIP_VIEWPORT_HORIZONTALLY": "Görünüm alanını yatay çevir",
+  "FLIP_VIEWPORT_VERTICALLY": "Görünüm alanını dikey çevir",
+  "SETTINGS": "Ayarlar",
+  "OPEN_SETTINGS": "Ayarları aç",
+  "OPEN_SETTINGS_DESCRIPTIVE": "Ayarlar penceresini aç",
+  "OPEN_STARTUP_WINDOW": "Başlangıç penceresini aç",
+  "OPEN_SHORTCUT_WINDOW": "Kısayollar penceresini aç",
+  "OPEN_ABOUT_WINDOW": "Hakkında penceresini aç",
+  "OPEN_PREVIEW_WINDOW": "Önizleme penceresini aç",
+  "ERROR": "Hata",
+  "INTERNAL_ERROR": "İç Hata",
+  "ERROR_SAVE_LOCATION": "Dosya belirtilen konuma kaydedilemedi",
+  "ERROR_WHILE_SAVING": "Kaydederken bir iç hata oluştu. Lütfen tekrar deneyin.",
+  "UNKNOWN_ERROR_SAVING": "Kaydederken bir hata oluştu.",
+  "FAILED_ASSOCIATE_LOSPEC": "Lospec Palet protokolü ilişkilendirilemedi.",
+  "REDDIT": "Reddit",
+  "GITHUB": "GitHub",
+  "YOUTUBE": "YouTube",
+  "DONATE": "Bağış Yap",
+  "YES": "Evet",
+  "NO": "Hayır",
+  "CANCEL": "İptal",
+  "UNNAMED": "Adsız",
+  "DELETE": "Sil",
+  "USER_PREFS": "Kullanıcı tercihleri (Roaming)",
+  "SHORTCUT_FILE": "Kısayol dosyası (Roaming)",
+  "EDITOR_DATA": "Editör verileri (Local)",
+  "MOVE_VIEWPORT_TOOLTIP": "Görünüm alanını taşır. ({0})",
+  "MOVE_VIEWPORT_ACTION_DISPLAY": "Görünüm alanını kaydırmak için tıklayın ve hareket ettirin",
+  "MOVE_TOOL_TOOLTIP": "Katmanları seç ve dönüştür ({0}).",
+  "MOVE_TOOL_ACTION_DISPLAY": "Seçili pikselleri taşımak için fareyi basılı tutun. Tüm katmanları taşımak için Ctrl tuşunu basılı tutun.",
+  "PEN_TOOL_TOOLTIP": "Kalem. ({0})",
+  "PEN_TOOL_ACTION_DISPLAY": "Çizmek için tıklayın ve hareket ettirin.",
+  "PIXEL_PERFECT_SETTING": "Piksel mükemmelliği",
+  "RECTANGLE_TOOL_TOOLTIP": "Tuval üzerine dikdörtgen çizer ({0}). Kare çizmek için Shift tuşunu basılı tutun.",
+  "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT": "Bir dikdörtgen çizmek için tıklayın ve hareket ettirin. Kare çizmek için Shift tuşunu basılı tutun.",
+  "RECTANGLE_TOOL_ACTION_DISPLAY_SHIFT": "Bir kare çizmek için tıklayın ve hareket ettirin.",
+  "KEEP_ORIGINAL_IMAGE_SETTING": "Orijinal resmi koru",
+  "ROTATE_VIEWPORT_TOOLTIP": "Görünüm alanını döndürür. ({0})",
+  "ROTATE_VIEWPORT_ACTION_DISPLAY": "Görünüm alanını döndürmek için tıklayın ve hareket ettirin",
+  "SELECT_TOOL_TOOLTIP": "Alan seçer. ({0})",
+  "SELECT_TOOL_ACTION_DISPLAY_DEFAULT": "Bir alan seçmek için tıklayın ve hareket ettirin. Mevcut seçime eklemek için Shift tuşunu basılı tutun. Ondan çıkarmak için Ctrl tuşunu basılı tutun.",
+  "SELECT_TOOL_ACTION_DISPLAY_SHIFT": "Mevcut seçime eklemek için tıklayın ve hareket ettirin.",
+  "SELECT_TOOL_ACTION_DISPLAY_CTRL": "Mevcut seçimden çıkarmak için tıklayın ve hareket ettirin.",
+  "ZOOM_TOOL_TOOLTIP": "Görünüm alanını yakınlaştırır ({0}). Yakınlaştırmak için tıklayın, uzaklaştırmak için alt tuşunu basılı tutup tıklayın.",
+  "ZOOM_TOOL_ACTION_DISPLAY_DEFAULT": "Yakınlaştırmak için tıklayın ve hareket ettirin. Yakınlaştırmak için tıklayın, uzaklaştırmak için ctrl tuşunu basılı tutup tıklayın.",
+  "ZOOM_TOOL_ACTION_DISPLAY_CTRL": "Yakınlaştırmak için tıklayın ve hareket ettirin. Uzaklaştırmak için tıklayın, yakınlaştırmak için ctrl tuşunu bırakıp tıklayın.",
+  "BRIGHTNESS_TOOL_TOOLTIP": "Pikselleri daha parlak veya daha koyu yapar ({0}). Pikselleri daha koyu yapmak için Ctrl tuşunu basılı tutun.",
+  "BRIGHTNESS_TOOL_ACTION_DISPLAY_DEFAULT": "Pikselleri daha parlak yapmak için üzerine çizin. Koyulaştırmak için Ctrl tuşunu basılı tutun.",
+  "BRIGHTNESS_TOOL_ACTION_DISPLAY_CTRL": "Pikselleri daha koyu yapmak için üzerine çizin. Parlatmak için Ctrl tuşunu bırakın.",
+  "COLOR_PICKER_TOOLTIP": "Tuvalden birincil rengi seçer. ({0})",
+  "COLOR_PICKER_ACTION_DISPLAY_DEFAULT": "Renkleri seçmek için tıklayın. Tuvali gizlemek için Ctrl tuşunu basılı tutun. Referans katmanını gizlemek için Shift tuşunu basılı tutun",
+  "ELLIPSE_TOOL_TOOLTIP": "Tuval üzerine bir elips çizer ({0}). Bir daire çizmek için Shift tuşunu basılı tutun.",
+  "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT": "Bir elips çizmek için fareyi tıklayın ve hareket ettirin. Bir daire çizmek için Shift tuşunu basılı tutun.",
+  "ELLIPSE_TOOL_ACTION_DISPLAY_SHIFT": "Bir daire çizmek için fareyi tıklayın ve hareket ettirin.",
+  "ERASER_TOOL_TOOLTIP": "Pikselden rengi siler. ({0})",
+  "ERASER_TOOL_ACTION_DISPLAY": "Silmek için tıklayın ve hareket ettirin.",
+  "FLOOD_FILL_TOOL_TOOLTIP": "Alanı renkle doldurur. ({0})",
+  "FLOOD_FILL_TOOL_ACTION_DISPLAY_DEFAULT": "Doldurmak için bir alana basın. Tüm katmanları dikkate almak için Ctrl tuşunu basılı tutun.",
+  "FLOOD_FILL_TOOL_ACTION_DISPLAY_CTRL": "Doldurmak için bir alana basın. Sadece mevcut katmanları dikkate almak için Ctrl tuşunu bırakın.",
+  "LASSO_TOOL_TOOLTIP": "Kement. ({0})",
+  "LASSO_TOOL_ACTION_DISPLAY_DEFAULT": "Kementin içindeki pikselleri seçmek için tıklayın ve hareket ettirin. Mevcut seçime eklemek için Shift tuşunu basılı tutun. Ondan çıkarmak için Ctrl tuşunu basılı tutun.",
+  "LASSO_TOOL_ACTION_DISPLAY_SHIFT": "Kementin içindeki pikselleri seçime eklemek için tıklayın ve hareket ettirin.",
+  "LASSO_TOOL_ACTION_DISPLAY_CTRL": "Kementin içindeki pikselleri seçimden çıkarmak için tıklayın ve hareket ettirin.",
+  "LINE_TOOL_TOOLTIP": "Tuval üzerine çizgi çizer ({0}). Hizalamayı etkinleştirmek için Shift tuşunu basılı tutun.",
+  "LINE_TOOL_ACTION_DISPLAY_DEFAULT": "Bir çizgi çizmek için tıklayın ve hareket ettirin. Hizalamayı etkinleştirmek için Shift tuşunu basılı tutun.",
+  "LINE_TOOL_ACTION_DISPLAY_SHIFT": "Hizalama etkinken bir çizgi çizmek için fareyi tıklayın ve hareket ettirin.",
+  "MAGIC_WAND_TOOL_TOOLTIP": "Sihirli Değnek ({0}). Seçimi doldurur",
+  "MAGIC_WAND_ACTION_DISPLAY": "Seçimi doldurmak için tıklayın.",
+  "PEN_TOOL": "Kalem",
+  "BRIGHTNESS_TOOL": "Parlaklık",
+  "COLOR_PICKER_TOOL": "Renk Seçici",
+  "ELLIPSE_TOOL": "Elips",
+  "ERASER_TOOL": "Silgi",
+  "FLOOD_FILL_TOOL": "Boya Kovası",
+  "LASSO_TOOL": "Kement",
+  "LINE_TOOL": "Çizgi",
+  "MAGIC_WAND_TOOL": "Sihirli Değnek",
+  "MOVE_TOOL": "Taşı",
+  "MOVE_VIEWPORT_TOOL": "Görünüm Alanını Taşı",
+  "RECTANGLE_TOOL": "Dikdörtgen",
+  "ROTATE_VIEWPORT_TOOL": "Görünüm Alanını Döndür",
+  "SELECT_TOOL_NAME": "Seç",
+  "ZOOM_TOOL": "Yakınlaştır",
+  "SHAPE_LABEL": "Şekil",
+  "MODE_LABEL": "Mod",
+  "SCOPE_LABEL": "Kapsam",
+  "FILL_SHAPE_LABEL": "Şekli doldur",
+  "FILL_COLOR_LABEL": "Dolgu rengi",
+  "TOOL_SIZE_LABEL": "Araç boyutu",
+  "STRENGTH_LABEL": "Güç",
+  "NEW": "Yeni",
+  "ADD": "Ekle",
+  "SUBTRACT": "Çıkar",
+  "INTERSECT": "Kesiştir",
+  "RECTANGLE": "Dikdörtgen",
+  "CIRCLE": "Daire",
+  "ABOUT": "Hakkında",
+  "MINIMIZE": "Simge Durumuna Küçült",
+  "RESTORE": "Geri Yükle",
+  "MAXIMIZE": "Ekranı Kapla",
+  "CLOSE": "Kapat",
+  "EXPORT_SIZE_HINT": "Resmi paylaşmak istiyorsanız, en iyi netlik için %{0} deneyin",
+  "CREATE": "Oluştur",
+  "BASE_LAYER_NAME": "Temel katman",
+  "ENABLE_MASK": "Maskeyi etkinleştir",
+  "SELECTED_AREA_EMPTY": "Seçili alan boş",
+  "NOTHING_TO_COPY": "Kopyalanacak bir şey yok",
+  "REFERENCE_LAYER_PATH": "Referans katmanı yolu",
+  "FLIP": "Çevir",
+  "ROTATION": "Döndürme",
+  "ROT_IMG_90_D": "Resmi 90° Döndür",
+  "ROT_IMG_180_D": "Resmi 180° Döndür",
+  "ROT_IMG_-90_D": "Resmi -90° Döndür",
+  "ROT_LAYERS_90_D": "Seçili Katmanları 90° Döndür",
+  "ROT_LAYERS_180_D": "Seçili Katmanları 180° Döndür",
+  "ROT_LAYERS_-90_D": "Seçili Katmanları -90° Döndür",
+  "UNNAMED_PALETTE": "Adsız Palet",
+  "CLICK_SELECT_PRIMARY": "Ana renk olarak seçmek için tıklayın.",
+  "PEN_MODE": "Kalem modu",
+  "VIEW": "Görünüm",
+  "HORIZONTAL_LINE_SYMMETRY": "Yatay çizgi simetrisi",
+  "VERTICAL_LINE_SYMMETRY": "Dikey çizgi simetrisi",
+  "COLOR_PICKER_TITLE": "Renk Seçici",
+  "COLOR_SLIDERS_TITLE": "Renk Kaydırıcıları",
+  "PALETTE_TITLE": "Palet",
+  "SWATCHES_TITLE": "Renk Örnekleri",
+  "LAYERS_TITLE": "Katmanlar",
+  "PREVIEW_TITLE": "Önizleme",
+  "NORMAL_BLEND_MODE": "Normal",
+  "ERASE_BLEND_MODE": "Sil",
+  "DARKEN_BLEND_MODE": "Koyulaştır",
+  "MULTIPLY_BLEND_MODE": "Çarp",
+  "COLOR_BURN_BLEND_MODE": "Renk Yanması",
+  "LIGHTEN_BLEND_MODE": "Açıklaştır",
+  "SCREEN_BLEND_MODE": "Ekran",
+  "COLOR_DODGE_BLEND_MODE": "Renk Soldurma",
+  "OVERLAY_BLEND_MODE": "Kaplama",
+  "SOFT_LIGHT_BLEND_MODE": "Yumuşak Işık",
+  "HARD_LIGHT_BLEND_MODE": "Sert Işık",
+  "DIFFERENCE_BLEND_MODE": "Fark",
+  "EXCLUSION_BLEND_MODE": "Dışlama",
+  "HUE_BLEND_MODE": "Ton",
+  "SATURATION_BLEND_MODE": "Doygunluk",
+  "LUMINOSITY_BLEND_MODE": "Parlaklık",
+  "COLOR_BLEND_MODE": "Renk",
+  "NOT_SUPPORTED_BLEND_MODE": "Desteklenmiyor",
+  "RESTART": "Yeniden Başlat",
+  "SORT_BY": "Sırala",
+  "NAME": "Ad",
+  "COLORS": "Renkler",
+  "DEFAULT": "Varsayılan",
+  "ALPHABETICAL": "Alfabetik",
+  "COLOR_COUNT": "Renk sayısı",
+  "ANY": "Herhangi biri",
+  "MAX": "Maks",
+  "MIN": "Min",
+  "EXACT": "Tam",
+  "ASCENDING": "Artan",
+  "DESCENDING": "Azalan",
+  "NAME_IS_TOO_LONG": "Ad çok uzun",
+  "STOP_IT_TEXT1": "Bu kadar yeter. Dosya adlarını düzenle.",
+  "STOP_IT_TEXT2": "Lütfen bu adları kopyalamayı bırakır mısın?",
+  "REPLACER_TOOLTIP": "Palet rengine sağ tıklayın ve 'Değiştir'i seçin veya buraya bırakın.",
+  "CLICK_TO_CHOOSE_COLOR": "Rengi seçmek için tıklayın",
+  "REPLACE_COLOR": "Rengi değiştir",
+  "PALETTE_COLOR_TOOLTIP": "Ana renk olarak seçmek için tıklayın. Değiştirmek için başka bir palet renginin üzerine sürükleyip bırakın.",
+  "ADD_FROM_SWATCHES": "Renk örneklerinden ekle",
+  "ADD_COLOR_TO_PALETTE": "Palete renk ekle",
+  "USE_IN_CURRENT_IMAGE": "Mevcut resimde kullan",
+  "ADD_TO_FAVORITES": "Favorilere ekle",
+  "BROWSE_PALETTES": "Paletlere göz at",
+  "LOAD_PALETTE": "Palet yükle",
+  "SAVE_PALETTE": "Paleti kaydet",
+  "FAVORITES": "Favoriler",
+  "ADD_FROM_CURRENT_PALETTE": "Mevcut paletten ekle",
+  "OPEN_PALETTES_DIR_TOOLTIP": "Paletler dizinini gezginde aç",
+  "BROWSE_ON_LOSPEC_TOOLTIP": "Lospec'te paletlere göz at",
+  "IMPORT_FROM_FILE_TOOLTIP": "Dosyadan içe aktar",
+  "TOP_LEFT": "Sol üst",
+  "TOP_CENTER": "Orta üst",
+  "TOP_RIGHT": "Sağ üst",
+  "MIDDLE_LEFT": "Sol orta",
+  "MIDDLE_CENTER": "Orta orta",
+  "MIDDLE_RIGHT": "Sağ orta",
+  "BOTTOM_LEFT": "Sol alt",
+  "BOTTOM_CENTER": "Orta alt",
+  "BOTTOM_RIGHT": "Sağ alt",
+  "CLIP_TO_BELOW": "Alttaki üyeye kırp",
+  "MOVE_UPWARDS": "Yukarı taşı",
+  "MOVE_DOWNWARDS": "Aşağı taşı",
+  "MERGE_SELECTED": "Seçilenleri birleştir",
+  "LOCK_TRANSPARENCY": "Şeffaflığı kilitle",
+  "COULD_NOT_LOAD_PALETTE": "Paletler getirilemedi",
+  "NO_PALETTES_FOUND": "Palet bulunamadı.",
+  "LOSPEC_LINK_TEXT": "Bazılarını burada bulabileceğinizi duydum: lospec.com/palette-list",
+  "PALETTE_BROWSER": "Palet Tarayıcısı",
+  "DELETE_PALETTE_CONFIRMATION": "Bu paleti silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
+  "SHORTCUTS_IMPORTED": "{0} konumundan kısayollar başarıyla içe aktarıldı.",
+  "SHORTCUT_PROVIDER_DETECTED": "{0} yüklü olduğunu tespit ettik. Kısayolları oradan içe aktarmak ister misiniz?",
+  "IMPORT_FROM_INSTALLATION": "Kurulumdan içe aktar",
+  "IMPORT_INSTALLATION_OPTION1": "Kurulumdan içe aktar",
+  "IMPORT_INSTALLATION_OPTION2": "Varsayılanları kullan",
+  "IMPORT_FROM_TEMPLATE": "Şablondan içe aktar",
+  "SHORTCUTS_IMPORTED_SUCCESS": "Kısayollar başarıyla içe aktarıldı.",
+  "WARNING_RESET_SHORTCUTS_DEFAULT": "Tüm kısayolları varsayılan değerlerine sıfırlamak istediğinizden emin misiniz?",
+  "SUCCESS": "Başarılı",
+  "WARNING": "Uyarı",
+  "ERROR_IMPORTING_IMAGE": "Resim içe aktarılırken bir hata oluştu.",
+  "SHORTCUTS_CORRUPTED_TITLE": "Bozuk kısayollar dosyası",
+  "SHORTCUTS_CORRUPTED": "Kısayollar dosyası bozulmuştu, varsayılana sıfırlanıyor.",
+  "FAILED_DOWNLOAD_PALETTE": "Palet indirilemedi",
+  "FILE_INCORRECT_FORMAT": "Dosya doğru biçimde değildi",
+  "INVALID_FILE": "Geçersiz dosya",
+  "SHORTCUTS_FILE_INCORRECT_FORMAT": "Kısayollar dosyası doğru biçimde değildi",
+  "UNSUPPORTED_FILE_FORMAT": "Bu dosya biçimi desteklenmiyor",
+  "ALREADY_ASSIGNED": "Zaten atanmış",
+  "REPLACE": "Değiştir",
+  "SWAP": "Takas Et",
+  "SHORTCUT_ALREADY_ASSIGNED_SWAP": "Bu kısayol zaten '{0}' öğesine atanmış\nMevcut kısayolu değiştirmek mi yoksa ikisini takas etmek mi istersiniz?",
+  "SHORTCUT_ALREADY_ASSIGNED_OVERWRITE": "Bu kısayol zaten '{0}' öğesine atanmış\nMevcut kısayolu değiştirmek ister misiniz?",
+  "UNSAVED_CHANGES": "Kaydedilmemiş değişiklikler",
+  "DOCUMENT_MODIFIED_SAVE": "Belge değiştirildi. Değişiklikleri kaydetmek ister misiniz?",
+  "SESSION_UNSAVED_DATA": "{0} kaydedilmemiş veri içeriyor. Emin misiniz?",
+  "PROJECT_MAINTAINERS": "Proje Sorumluları",
+  "OTHER_AWESOME_CONTRIBUTORS": "Ve diğer harika katkıda bulunanlar",
+  "HELP": "Yardım",
+  "STOP_IT_TEXT3": "Hayır, gerçekten, dur.",
+  "STOP_IT_TEXT4": "Yapacak daha iyi bir işin yok mu?",
+  "LINEAR_DODGE_BLEND_MODE": "Doğrusal Soldurma (Ekle)",
+  "PRESS_ANY_KEY": "Herhangi bir tuşa basın",
+  "NONE_SHORTCUT": "Yok",
+  "REFERENCE": "Referans",
+  "PUT_REFERENCE_LAYER_ABOVE": "Referans katmanını üste koy",
+  "PUT_REFERENCE_LAYER_BELOW": "Referans katmanını alta koy",
+  "TOGGLE_VERTICAL_SYMMETRY": "Dikey simetriyi aç/kapat",
+  "TOGGLE_HORIZONTAL_SYMMETRY": "Yatay simetriyi aç/kapat",
+  "RESET_VIEWPORT": "Görünüm alanını sıfırla",
+  "VIEWPORT_SETTINGS": "Görünüm alanı ayarları",
+  "MOVE_TOOL_ACTION_DISPLAY_TRANSFORMING": "Seçili katmanlardaki pikselleri taşımak için fareyi tıklayıp basılı tutun.",
+  "CTRL_KEY": "Ctrl",
+  "SHIFT_KEY": "Shift",
+  "ALT_KEY": "Alt",
+  "RENAME": "Yeniden Adlandır",
+  "PIXEL_UNIT": "px",
+  "OPEN_LOCALIZATION_DEBUG_WINDOW": "Yerelleştirme Hata Ayıklama Penceresini Aç",
+  "FORCE_OTHER_FLOW_DIRECTION": "Diğer akış yönünü zorla",
+  "API_KEY": "API Anahtarı",
+  "LOCALIZATION_VIEW_TYPE": "Yerelleştirme Görünüm Türü",
+  "LOAD_LANGUAGE_FROM_FILE": "Dili dosyadan yükle",
+  "LOG_IN": "Giriş Yap",
+  "SYNC": "Senkronize Et",
+  "NOT_LOGGED_IN": "Giriş yapılmadı",
+  "POE_EDITOR_ERROR": "POEditor Hatası: {0} {1}",
+  "HTTP_ERROR_MESSAGE": "HTTP Hatası: {0} {1}",
+  "LOGGED_IN": "Giriş yapıldı",
+  "SYNCED_SUCCESSFULLY": "Başarıyla senkronize edildi",
+  "EXCEPTION_ERROR": "İstisna: {0}",
+  "DROP_PALETTE": "Paleti buraya bırakın",
+  "SECURITY_ERROR": "Güvenlik hatası",
+  "SECURITY_ERROR_MSG": "Belirtilen konuma yazma hakkı yok.",
+  "IO_ERROR": "G/Ç hatası",
+  "IO_ERROR_MSG": "Diske yazarken hata oluştu.",
+  "FAILED_ASSOCIATE_PIXI": ".pixi dosyası PixiEditor ile ilişkilendirilemedi.",
+  "COULD_NOT_SAVE_PALETTE": "Palet kaydedilirken bir hata oluştu.",
+  "NO_COLORS_TO_SAVE": "Kaydedilecek renk yok.",
+  "CANVAS": "Tuval",
+  "SINGLE_LAYER": "Tek Katman",
+  "CHOOSE": "Seç",
+  "REMOVE": "Kaldır",
+  "FILE_FORMAT_NOT_ASEPRITE_KEYS": "Dosya bir \".aseprite-keys\" dosyası değil",
+  "FILE_HAS_INVALID_SHORTCUT": "Dosya geçersiz bir kısayol içeriyor",
+  "FILE_EXTENSION_NOT_SUPPORTED": "'{0}' dosya türü desteklenmiyor",
+  "ERROR_READING_FILE": "Dosya okunurken hata oluştu",
+  "DISCARD_PALETTE": "Paleti at",
+  "DISCARD_PALETTE_CONFIRMATION": "Mevcut paleti atmak istediğinizden emin misiniz? Bu işlem geri alınamaz.",
+  "IMPORT_AS_NEW_LAYER": "Yeni katman olarak içe aktar",
+  "PASTE_AS_PRIMARY_COLOR": "Birincil renk olarak yapıştır",
+  "IMPORT_AS_NEW_FILE": "Yeni dosya olarak içe aktar",
+  "IMPORT_PALETTE_FILE": "Palet dosyasını içe aktar",
+  "IMPORT_MULTIPLE_PALETTE_COLORS": "Renkleri palete aktar",
+  "IMPORT_SINGLE_PALETTE_COLOR": "Rengi palete aktar",
+  "IMPORT_AS_REFERENCE_LAYER": "Referans katmanı olarak içe aktar",
+  "NAVIGATOR_PICK_ACTION_DISPLAY": "Renk seçmek için sağ tıklayın, rengi panoya kopyalamak için Shift-sağ tıklayın",
+  "OPEN_FILE_FROM_CLIPBOARD": "Panodan aç",
+  "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE": "Panodan aç",
+  "OPEN_LOCALIZATION_DATA": "LocalizationData.json dosyasını açmak istiyor musunuz?\nGüncellenmiş tarih panoya kopyalandı.\nDeğişikliklerin yeniden başlatılana kadar uygulanmayacağını unutmayın",
+  "DOWNLOADING_LANGUAGE_FAILED": "Dil indirilemedi.\nAPI Anahtarı aşırı kullanılmış olabilir.",
+  "LOCALIZATION_DATA_NOT_FOUND": "Yerelleştirme veri yolu bulunamadı",
+  "APPLY": "Uygula",
+  "UPDATE_SOURCE": "Kaynağı güncelle",
+  "COPY_TO_CLIPBOARD": "Panoya kopyala",
+  "LANGUAGE_FILE_NOT_FOUND": "Dil dosyası bulunamadı.\nAranan: {0}",
+  "PROJECT_ROOT_NOT_FOUND": "PixiEditor Proje kökü bulunamadı.\nAranan: PixiEditor.csproj",
+  "LOCALIZATION_FOLDER_NOT_FOUND": "Yerelleştirme klasörü bulunamadı.\nAranan: /Data/Localization",
+  "SELECT_A_LANGUAGE": "Bir dil seçin",
+  "DONE": "Bitti",
+  "SOURCE_UNSET_OR_MISSING": "Kaynak eksik/ayarlanmamış",
+  "SOURCE_NEWER": "Kaynak daha yeni",
+  "SOURCE_UP_TO_DATE": "Kaynak güncel",
+  "SOURCE_OLDER": "Bulut daha yeni",
+  "COLOR_PICKER_ACTION_DISPLAY_REFERENCE_ONLY": "Referans katmanından renkleri seçmek için tıklayın.",
+  "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Tuvalden renkleri seçmek için tıklayın.",
+  "LOCALIZATION_DEBUG_WINDOW_TITLE": "Yerelleştirme Hata Ayıklama Penceresi",
+  "COMMAND_DEBUG_WINDOW_TITLE": "Komut Hata Ayıklama Penceresi",
+  "SHORTCUTS_TITLE": "Kısayollar",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_PERSPECTIVE": "Dönüşümü ölçeklemek için tutamaçları sürükleyin. Merkezden ölçeklemek için Ctrl tuşunu basılı tutun ve bir tutamacı sürükleyin. Orantılı olarak ölçeklemek için Shift tuşunu basılı tutun. Eğim vermek için Alt tuşunu basılı tutun ve bir yan tutamacı sürükleyin. Döndürmek için dış tutamaçları sürükleyin.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Dönüşümü ölçeklemek için tutamaçları sürükleyin. Merkezden ölçeklemek için Ctrl tuşunu basılı tutun ve bir tutamacı sürükleyin. Orantılı olarak ölçeklemek için Shift tuşunu basılı tutun. Eğim vermek için Alt tuşunu basılı tutun ve bir yan tutamacı sürükleyin. Döndürmek için dış tutamaçları sürükleyin.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Dönüşümü ölçeklemek için tutamaçları sürükleyin. Merkezden ölçeklemek için Ctrl tuşunu basılı tutun ve bir tutamacı sürükleyin. Orantılı olarak ölçeklemek için Shift tuşunu basılı tutun. Döndürmek için dış tutamaçları sürükleyin.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Dönüşümü ölçeklemek için tutamaçları sürükleyin. Merkezden ölçeklemek için Ctrl tuşunu basılı tutun ve bir tutamacı sürükleyin. Orantılı olarak ölçeklemek için Shift tuşunu basılı tutun.",
+  "LOCAL_PALETTE_SOURCE_NAME": "Yerel",
+  "ERROR_FORBIDDEN_UNIQUE_NAME": "Uzantı benzersiz adı 'pixieditor' ile başlayamaz.",
+  "ERROR_MISSING_METADATA": "Uzantı meta veri anahtarı '{0}' eksik.",
+  "ERROR_NO_CLASS_ENTRY": "Uzantı sınıf girişi '{0}' yolunda eksik.",
+  "ERROR_NO_ENTRY_ASSEMBLY": "Uzantı giriş derlemesi '{0}' yolunda eksik.",
+  "ERROR_MISSING_ADDITIONAL_CONTENT": "Mevcut kurulumunuz bu uzantının yüklenmesine izin vermiyor. Belki de sahip değilsiniz veya kurulu değil. Buradan satın alabilirsiniz: '{0}'.",
+  "BUY_SUPPORTER_PACK": "Destekçi Paketini Satın Al",
+  "NEWS": "Haberler",
+  "DISABLE_NEWS_PANEL": "Başlangıç penceresindeki Haberler panelini devre dışı bırak",
+  "FAILED_FETCH_NEWS": "Haberler getirilemedi",
+  "CROP_TO_SELECTION": "Seçime göre kırp",
+  "CROP_TO_SELECTION_DESCRIPTIVE": "Resmi seçime göre kırp",
+  "SHOW_CONTEXT_MENU": "Bağlam menüsünü göster",
+  "ERASE": "Sil",
+  "USE_SECONDARY_COLOR": "İkincil rengi kullan",
+  "RIGHT_CLICK_MODE": "Sağ tıklama modu",
+  "ADD_PRIMARY_COLOR_TO_PALETTE": "Birincil rengi palete ekle",
+  "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Birincil rengi mevcut palete ekle",
+  "EXPORT_SAVE_TITLE": "Resmi kaydetmek için bir konum seçin",
+  "BROWSE_DIRECTORY": "Dizine Göz At",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Tüm belgeler kurtarılamadı",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Tüm belgeler kurtarılamadı. Çalışmalarınızı kaydetmede daha iyi olun.",
+  "SEND": "Raporu Gönder",
+  "OPEN_DOCKABLE_MENU": "Sekmeyi Aç",
+  "TIMELINE_TITLE": "Zaman Çizelgesi",
+  "EXPORT_IMAGE_HEADER": "Resim",
+  "EXPORT_ANIMATION_HEADER": "Animasyon",
+  "EXPORT_SPRITESHEET_HEADER": "Spritesheet",
+  "PIXI_FILE": "PixiEditor Dosyaları",
+  "PNG_FILE": "PNG Resimleri",
+  "JPEG_FILE": "JPEG Resimleri",
+  "WEBP_FILE": "WebP Resimleri",
+  "GIF_FILE": "GIF'ler",
+  "BMP_FILE": "BMP Resimleri",
+  "IMAGE_FILES": "Resim Dosyaları",
+  "VIDEO_FILES": "Video Dosyaları",
+  "OPEN_TYPE_FONT": "OpenType Yazı Tipleri",
+  "TRUE_TYPE_FONT": "TrueType Yazı Tipleri",
+  "SVG_FILE": "Ölçeklenebilir Vektör Grafikleri",
+  "MP4_FILE": "MP4 Videoları",
+  "COLUMNS": "Sütunlar",
+  "ROWS": "Satırlar",
+  "BACKGROUND": "Arka Plan",
+  "OPACITY": "Opaklık",
+  "IS_VISIBLE": "Görünür",
+  "CLIP_TO_MEMBER_BELOW": "Alttaki üyeye kırp",
+  "BLEND_MODE": "Karıştırma modu",
+  "MASK": "Maske",
+  "MASK_IS_VISIBLE": "Maske görünür",
+  "OUTPUT": "Çıktı",
+  "INPUT": "Girdi",
+  "NODE_GRAPH_TITLE": "Düğüm Grafiği",
+  "CONTENT": "İçerik",
+  "RADIUS": "Yarıçap",
+  "STROKE_COLOR": "Kontur rengi",
+  "STROKE_WIDTH": "Kontur genişliği",
+  "FILL_COLOR": "Dolgu rengi",
+  "TOP": "Üst",
+  "BOTTOM": "Alt",
+  "CHANNELS_DOCK_TITLE": "Kanallar",
+  "RED": "Kırmızı",
+  "GREEN": "Yeşil",
+  "BLUE": "Mavi",
+  "ALPHA": "Alfa",
+  "COLOR": "Renk",
+  "COORDINATE": "Koordinat",
+  "VECTOR": "Vektör",
+  "MATRIX": "Matris",
+  "TRANSFORMED": "Dönüştürülmüş",
+  "GRAYSCALE": "Gri Tonlama",
+  "CLAMP": "Sıkıştır",
+  "SIZE": "Boyut",
+  "NOISE": "Gürültü",
+  "SCALE": "Ölçek",
+  "SEED": "Tohum",
+  "KERNEL": "Çekirdek",
+  "KERNEL_VIEW_SUM": "Toplam:",
+  "KERNEL_VIEW_SUM_TOOLTIP": "Tüm değerlerin toplamı. Muhtemelen 1 veya 0 değerini hedeflemek istersiniz",
+  "GAIN": "Kazanç",
+  "BIAS": "Eğilim",
+  "TILE_MODE": "Döşeme Modu",
+  "ON_ALPHA": "Alfa Üzerinde",
+  "PIXEL_COORDINATE": "Piksel Koordinatı",
+  "OUTPUT_NODE": "Çıktı",
+  "NOISE_NODE": "Gürültü",
+  "ELLIPSE_NODE": "Elips",
+  "CREATE_IMAGE_NODE": "Resim Oluştur",
+  "FOLDER_NODE": "Klasör",
+  "IMAGE_LAYER_NODE": "Resim Katmanı",
+  "KERNEL_FILTER_NODE": "Çekirdek Filtresi",
+  "MATH_NODE": "Matematik",
+  "COLOR_MATRIX_TRANSFORM_FILTER_NODE": "Matris Dönüşüm Filtresi",
+  "MERGE_NODE": "Birleştir",
+  "MODIFY_IMAGE_LEFT_NODE": "Resmi Değiştirmeye Başla",
+  "MODIFY_IMAGE_RIGHT_NODE": "Resmi Değiştirmeyi Bitir",
+  "MODIFY_IMAGE_PAIR_NODE": "Resmi Değiştir",
+  "COMBINE_CHANNELS_NODE": "Kanalları Birleştir",
+  "COMBINE_COLOR_NODE": "Rengi Birleştir",
+  "COMBINE_VECD_NODE": "Vektörü Birleştir",
+  "COMBINE_VECI_NODE": "Tamsayı Vektörünü Birleştir",
+  "SEPARATE_CHANNELS_NODE": "Kanalları Ayır",
+  "SEPARATE_VECD_NODE": "Vektörü Ayır",
+  "SEPARATE_VECI_NODE": "Tamsayı Vektörünü Ayır",
+  "SEPARATE_COLOR_NODE": "Rengi Ayır",
+  "TIME_NODE": "Zaman",
+  "FILTERS": "Filtreler",
+  "PREVIOUS": "Önceki",
+  "FILL": "Doldur",
+  "MATH_MODE": "Matematik Modu",
+  "NOISE_TYPE": "Gürültü Tipi",
+  "OCTAVES": "Oktavlar",
+  "ACTIVE_FRAME": "Aktif Kare",
+  "NORMALIZED_TIME": "Normalleştirilmiş Zaman",
+  "WITHOUT_FILTERS": "Filtresiz",
+  "RAW_LAYER_OUTPUT": "Ham",
+  "EXAMPLE_FILES": "Örnek Dosyalar",
+  "PROCEDURAL_GENERATION": "Prosedürel Animasyon",
+  "POND_EXAMPLE": "Göl",
+  "TREE_EXAMPLE": "Rüzgarlı Ağaç",
+  "OUTLINE_EXAMPLE": "Otomatik Anahat",
+  "BETA_ANIMATIONS": "Animasyonlar",
+  "SLIME_EXAMPLE": "Animasyonlu Balçık",
+  "SHOW_ALL_EXAMPLES": "Tümünü göster",
+  "APPLY_FILTER_NODE": "Filtre Uygula",
+  "FILTER": "Filtre",
+  "LERP_NODE": "Lerp",
+  "GRAYSCALE_FILTER_NODE": "Gri Tonlama Filtresi",
+  "FROM": "Başlangıç",
+  "TO": "Bitiş",
+  "TIME": "Zaman",
+  "WARMING_UP": "Isınıyor",
+  "RENDERING_FRAME": "Kare Oluşturuluyor {0}/{1}",
+  "RENDERING_VIDEO": "Video Oluşturuluyor",
+  "FINISHED": "Bitti",
+  "GENERATING_SPRITE_SHEET": "Sprite Sayfası Oluşturuluyor",
+  "RENDERING_IMAGE": "Resim Oluşturuluyor",
+  "PROGRESS_POPUP_TITLE": "İlerleme",
+  "POINTS": "Noktalar",
+  "MIN_DISTANCE": "Min. Mesafe",
+  "MAX_POINTS": "Maks. Nokta",
+  "PROBABILITY": "Olasılık",
+  "DISTRIBUTE_POINTS": "Noktaları dağıt",
+  "REMOVE_CLOSE_POINTS": "Yakın noktaları kaldır",
+  "RASTERIZE_SHAPE": "Şekli Rasterleştir",
+  "MODE": "Mod",
+  "Factor": "Faktör",
+  "NORMALIZE": "Normalleştir",
+  "WEIGHT_FACTOR": "Ağırlık",
+  "STARS_EXAMPLE": "Yıldızlar",
+  "ADD_EMPTY_FRAME": "Boş kare ekle",
+  "DUPLICATE_FRAME": "Kareyi çoğalt",
+  "DELETE_FRAME": "Kareyi kaldır",
+  "DEFAULT_MEMBER_NAME": "Yeni Eleman",
+  "NO_PARSER_FOUND": "'{0}' uzantısı için dosya ayrıştırıcı bulunamadı",
+  "SELECT_FILE_FORMAT": "Dosya biçimini seç",
+  "SELECT_FILE_FORMAT_DESCRIPTION": "Aynı biçimde birden fazla dosya türü desteklenmektedir. Lütfen kullanmak istediğinizi seçin.",
+  "NEW_PALETTE_FILE": "palet",
+  "ISLAND_EXAMPLE": "Adalar",
+  "ONION_FRAMES_COUNT": "Soğan katman sayısı",
+  "ONION_OPACITY": "Soğan opaklığı",
+  "TOGGLE_ONION_SKINNING": "Soğan görünümünü aç/kapat",
+  "CHANGE_ACTIVE_FRAME_PREVIOUS": "Aktif kareyi bir öncekine değiştir",
+  "CHANGE_ACTIVE_FRAME_NEXT": "Aktif kareyi bir sonrakine değiştir",
+  "TOGGLE_ANIMATION": "Animasyonu aç/kapat",
+  "NEW_FROM_CLIPBOARD": "Panodan yeni",
+  "OFFSET": "Ofset",
+  "SHAPE": "Şekil",
+  "STRUCTURE": "Yapı",
+  "NUMBERS": "Sayılar",
+  "OPERATIONS": "İşlemler",
+  "GENERATION": "Oluşturma",
+  "NUMBER": "Sayı",
+  "ANIMATION": "Animasyon",
+  "SAMPLE_IMAGE": "Örnek Resim",
+  "POSITION": "Konum",
+  "MATH_ADD": "Topla",
+  "MATH_SUBTRACT": "Çıkar",
+  "MULTIPLY": "Çarp",
+  "DIVIDE": "Böl",
+  "SIN": "Sin",
+  "COS": "Cos",
+  "TAN": "Tan",
+  "GREATER_THAN": "Büyüktür",
+  "LESS_THAN": "Küçüktür",
+  "LESS_THAN_OR_EQUAL": "Küçüktür veya eşittir",
+  "COMPARE": "Karşılaştır",
+  "MATH_POWER": "Üs",
+  "LOGARITHM": "Logaritma",
+  "NATURAL_LOGARITHM": "Doğal logaritma",
+  "ROOT": "Kök",
+  "INVERSE_ROOT": "Ters kök",
+  "FRACTION": "Kesir",
+  "NEGATE": "Olumsuzla",
+  "FLOOR": "Taban",
+  "CEIL": "Tavan",
+  "ROUND": "Yuvarla",
+  "MODULO": "Modulo",
+  "STEP": "Adım",
+  "SMOOTH_STEP": "Yumuşak Adım",
+  "PIXEL_ART_TOOLSET": "Piksel Sanatı",
+  "VECTOR_TOOLSET": "Vektör",
+  "VECTOR_LAYER": "Vektör Katmanı",
+  "STROKE_COLOR_LABEL": "Kontur",
+  "SYNC_WITH_PRIMARY_COLOR_LABEL": "Birincil renkle senkronize et",
+  "RASTERIZE": "Rasterleştir",
+  "RASTERIZE_ACTIVE_LAYER": "Aktif katmanı rasterleştir",
+  "RASTERIZE_ACTIVE_LAYER_DESCRIPTIVE": "Aktif katmanı bir resim (raster) katmanına dönüştür/rasterleştir.",
+  "NEW_ELLIPSE_LAYER_NAME": "Elips",
+  "NEW_RECTANGLE_LAYER_NAME": "Dikdörtgen",
+  "NEW_LINE_LAYER_NAME": "Çizgi",
+  "RENDER_OUTPUT": "Çıktıyı Oluştur",
+  "PAINT_TOOLSET": "Boya",
+  "HARDNESS_SETTING": "Sertlik",
+  "SPACING_SETTING": "Aralık",
+  "ANTI_ALIASING_SETTING": "Kenar Yumuşatma",
+  "TOLERANCE_LABEL": "Tolerans",
+  "TOGGLE_SNAPPING": "Hizalamayı aç/kapat",
+  "HIGH_RES_PREVIEW": "Yüksek Çözünürlüklü Önizleme",
+  "LOW_RES_PREVIEW": "Belge Çözünürlüğünde Önizleme",
+  "TOGGLE_HIGH_RES_PREVIEW": "Yüksek çözünürlüklü önizlemeyi aç/kapat",
+  "FACTOR": "Faktör",
+  "PATH_TOOL": "Yol",
+  "PATH_TOOL_TOOLTIP": "Vektör yolları ve eğrileri oluşturun ({0}).",
+  "PATH_TOOL_ACTION_DISPLAY": "Bir nokta eklemek için tıklayın.",
+  "PATH_TOOL_ACTION_DISPLAY_CTRL": "Mevcut bir noktaya tıklayın ve bir eğri yapmak için sürükleyin. Seçmek için bir kontrol noktasına dokunun.",
+  "PATH_TOOL_ACTION_DISPLAY_SHIFT": "Yeni bir katman oluşturmak için tıklayın.",
+  "PATH_TOOL_ACTION_DISPLAY_CTRL_SHIFT": "Seçime eklemek için bir kontrol noktasına dokunun.",
+  "PATH_TOOL_ACTION_DISPLAY_ALT": "Eğrinin sadece bir tarafını ayarlamak için bir kontrol noktasına tıklayın ve hareket ettirin.",
+  "DEFAULT_PATH_LAYER_NAME": "Yol",
+  "DELETE_NODES": "Düğümleri sil",
+  "DELETE_NODES_DESCRIPTIVE": "Seçili düğümleri sil",
+  "DELETE_CELS": "Kareleri sil",
+  "DELETE_CELS_DESCRIPTIVE": "Seçili kareleri sil",
+  "COPY_COLOR_TO_CLIPBOARD": "Rengi panoya kopyala",
+  "VIEWPORT_ROTATION": "Görünüm alanı döndürme",
+  "NEXT_TOOL_SET": "Sonraki araç seti",
+  "PREVIOUS_TOOL_SET": "Önceki araç seti",
+  "FILL_MODE": "Doldurma modu",
+  "USE_LINEAR_SRGB_PROCESSING": "Renkleri işlemek için doğrusal sRGB kullan",
+  "USE_LINEAR_SRGB_PROCESSING_DESC": "Renkleri işlemek için sRGB harmanlama modunu kullanarak belgeyi doğrusal sRGB'ye dönüştürün. Bu, belgenin renklerini etkileyecek, ancak harmanlamayı daha doğru hale getirecektir.",
+  "FILL_TYPE_WINDING": "Sarma",
+  "FILL_TYPE_EVEN_ODD": "Çift Tek",
+  "FILL_TYPE_INVERSE_WINDING": "Ters Sarma",
+  "FILL_TYPE_INVERSE_EVEN_ODD": "Ters Çift Tek",
+  "STROKE_CAP": "Kontür Ucu",
+  "STROKE_JOIN": "Kontür Birleşimi",
+  "COPY_VISIBLE": "Görünürü kopyala",
+  "COPY_VISIBLE_DESCRIPTIVE": "Görünür pikselleri kopyala",
+  "COLOR_SAMPLE_MODE": "Örnek modu",
+  "CREATE_CEL": "Kare oluştur",
+  "CREATE_CEL_DESCRIPTIVE": "Yeni bir kare oluştur",
+  "DUPLICATE_CEL": "Kareyi çoğalt",
+  "DUPLICATE_CEL_DESCRIPTIVE": "Mevcut karede kareyi çoğalt",
+  "RENDER_PREVIEW": "Önizlemeyi oluştur",
+  "OUTPUT_NAME": "Çıktı adı",
+  "CUSTOM_OUTPUT_NODE": "Özel Çıktı",
+  "TOGGLE_HUD": "HUD'u aç/kapat",
+  "OPEN_TIMELINE": "Zaman çizelgesini aç",
+  "OPEN_NODE_GRAPH": "Düğüm grafiğini aç",
+  "TOGGLE_PLAY": "Animasyonu Oynat/Duraklat",
+  "COPY_NODES": "Düğümleri kopyala",
+  "COPY_NODES_DESCRIPTIVE": "Seçili düğümleri kopyala",
+  "PASTE_NODES": "Düğümleri yapıştır",
+  "PASTE_NODES_DESCRIPTIVE": "Kopyalanan düğümleri yapıştır",
+  "COPY_CELS": "Kareleri kopyala",
+  "COPY_CELS_DESCRIPTIVE": "Seçili kareleri kopyala",
+  "TOGGLE_ONION_SKINNING_DESCRIPTIVE": "Soğan zarı görünümünü aç/kapat",
+  "VALUE": "Değer",
+  "TARGET": "Hedef",
+  "EPSILON": "Epsilon",
+  "PRESERVE_ALPHA": "Alfayı koru",
+  "BLUR_FILTER_NODE": "Gauss Bulanıklık Filtresi",
+  "LENGTH": "Uzunluk",
+  "GREATER_THAN_OR_EQUAL": "Büyüktür veya eşittir",
+  "COLOR_NODE": "Renk",
+  "CONVERT_TO_CURVE": "Eğriye dönüştür",
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Seçili vektör katmanını bir eğriye/yola dönüştür",
+  "FONT_FILES": "Yazı Tipi Dosyaları",
+  "UNIT_PT": "pt",
+  "FONT_LABEL": "Aile",
+  "FONT_SIZE_LABEL": "Boyut",
+  "SPACING_LABEL": "Aralık",
+  "TEXT_TOOL": "Metin",
+  "MISSING_FONT": "Eksik yazı tipi",
+  "TEXT_LAYER_NAME": "Metin",
+  "TEXT_TOOL_TOOLTIP": "Metin oluştur ({0}).",
+  "BOLD_TOOLTIP": "Kalın",
+  "ITALIC_TOOLTIP": "İtalik",
+  "CUSTOM_FONT": "Özel yazı tipi",
+  "DUMP_GPU_DIAGNOSTICS": "GPU tanılamalarını dök",
+  "USE_SRGB_PROCESSING": "Renkleri işlemek için sRGB kullan",
+  "USE_SRGB_PROCESSING_DESC": "Renkleri işlemek için doğrusal sRGB kullanarak belgeyi sRGB'ye dönüştürün. Bu, belgenin renklerini etkileyecektir.",
+  "TEXT_NODE": "Metin",
+  "TEXT_LABEL": "Metin",
+  "TEXT_ON_PATH_NODE": "Yol Üzerinde Metin",
+  "HIGH_DPI_RENDERING": "Yüksek DPI Oluşturma",
+  "THICKNESS": "Kalınlık",
+  "TYPE": "Tür",
+  "EFFECTS": "Efektler",
+  "OUTLINE_NODE": "Anahat",
+  "SHADER_CODE": "Gölgelendirici Kodu",
+  "SHADER_NODE": "Gölgelendirici",
+  "FAILED_TO_OPEN_EDITABLE_STRING_TITLE": "Dosya açılamadı",
+  "FAILED_TO_OPEN_EDITABLE_STRING_MESSAGE": "Bu dize harici düzenleyicide düzenlenemedi. Sebep: {0}",
+  "STRING_EDIT_IN_DEFAULT_APP": "Varsayılan uygulamada düzenle",
+  "STRING_OPEN_IN_FOLDER": "Klasörde aç",
+  "DISCO_BALL_EXAMPLE": "Disko Topu",
+  "COLOR_SPACE": "Renk Uzayı",
+  "PHOTO_EXAMPLES": "Fotoğraf",
+  "MASK_EXAMPLE": "Maske",
+  "SHADOW_NODE": "Gölge Filtresi",
+  "INPUT_MATRIX": "Giriş Matrisi",
+  "OUTPUT_MATRIX": "Çıkış Matrisi",
+  "CENTER": "Merkez",
+  "CONTENT_OFFSET": "İçerik Ofseti",
+  "CANVAS_POSITION": "Tuval Konumu",
+  "CENTER_POSITION": "Merkez Konumu",
+  "TILE_MODE_X": "Döşeme Modu X",
+  "TILE_MODE_Y": "Döşeme Modu Y",
+  "TILE_NODE": "Döşeme",
+  "SKEW": "Eğme",
+  "OFFSET_NODE": "Ofset",
+  "SKEW_NODE": "Eğme",
+  "ROTATION_NODE": "Döndürme",
+  "SCALE_NODE": "Ölçek",
+  "ROTATE_NODE": "Döndür",
+  "TRANSFORM_NODE": "Dönüştür",
+  "UNIT": "Birim",
+  "ANGLE": "Açı",
+  "DOCUMENT_INFO_NODE": "Belge Bilgisi",
+  "MASK_NODE": "Maske",
+  "SEPIA_FILTER_NODE": "Sepya Filtresi",
+  "INTENSITY": "Yoğunluk",
+  "INVERT_FILTER_NODE": "Ters Çevirme Filtresi",
+  "COLOR_ADJUSTMENTS_FILTER": "Renk Ayarları Filtresi",
+  "ADJUST_BRIGHTNESS": "Parlaklığı Ayarla",
+  "ADJUST_CONTRAST": "Kontrastı Ayarla",
+  "ADJUST_SATURATION": "Doygunluğu Ayarla",
+  "ADJUST_TEMPERATURE": "Sıcaklığı Ayarla",
+  "ADJUST_TINT": "Rengi Ayarla",
+  "ADJUST_HUE": "Tonu Ayarla",
+  "HUE_VALUE": "Ton",
+  "SATURATION_VALUE": "Doygunluk",
+  "BRIGHTNESS_VALUE": "Parlaklık",
+  "CONTRAST_VALUE": "Kontrast",
+  "TEMPERATURE_VALUE": "Sıcaklık",
+  "TINT_VALUE": "Renk Tonu",
+  "FAILED_DOWNLOADING_UPDATE_TITLE": "Güncelleme indirilemedi",
+  "FAILED_DOWNLOADING_UPDATE": "Güncelleme indirilemedi. Daha sonra tekrar deneyin.",
+  "UNEXPECTED_SHUTDOWN": "Beklenmedik kapanma",
+  "UNEXPECTED_SHUTDOWN_MSG": "PixiEditor beklenmedik bir şekilde kapandı. Dosyalarınızın en son otomatik kaydını yükledik.",
+  "OK": "Tamam",
+  "OPEN_AUTOSAVES": "Otomatik Kayıtlara Göz At",
+  "AUTOSAVE_SETTINGS_HEADER": "Otomatik Kaydetme",
+  "AUTOSAVE_SETTINGS_SAVE_STATE": "Başlangıçta son dosyaları yeniden aç",
+  "AUTOSAVE_SETTINGS_PERIOD": "Otomatik kaydetme periyodu",
+  "AUTOSAVE_ENABLED": "Otomatik kaydetme etkin",
+  "MINUTE_UNIVERSAL": "dk",
+  "AUTOSAVE_SETTINGS_SAVE_USER_FILE": "Seçili dosyaya otomatik kaydet",
+  "LOAD_LAZY_FILE_MESSAGE": "Başlatma süresini iyileştirmek için, PixiEditor bu dosyayı yüklemedi. Yüklemek için aşağıdaki düğmeye tıklayın.",
+  "EASING_NODE": "Yumuşatma",
+  "EASING_TYPE": "Yumuşatma Türü",
+  "OPEN_DIRECTORY_ON_EXPORT": "Dışa aktarma sırasında dizini aç",
+  "ERROR_LOOP_DETECTED_MESSAGE": "Bu katmanı taşımak bir döngü oluşturacaktır. Düğüm Grafiği'nde düzeltin.",
+  "LINEAR_EASING_TYPE": "Doğrusal",
+  "IN_SINE_EASING_TYPE": "İçeri Sinüs",
+  "OUT_SINE_EASING_TYPE": "Dışarı Sinüs",
+  "IN_OUT_SINE_EASING_TYPE": "İçeri Dışarı Sinüs",
+  "IN_QUAD_EASING_TYPE": "İçeri Karesel",
+  "OUT_QUAD_EASING_TYPE": "Dışarı Karesel",
+  "IN_OUT_QUAD_EASING_TYPE": "İçeri Dışarı Karesel",
+  "IN_CUBIC_EASING_TYPE": "İçeri Kübik",
+  "OUT_CUBIC_EASING_TYPE": "Dışarı Kübik",
+  "IN_OUT_CUBIC_EASING_TYPE": "İçeri Dışarı Kübik",
+  "IN_QUART_EASING_TYPE": "İçeri Dördüncü Dereceden",
+  "OUT_QUART_EASING_TYPE": "Dışarı Dördüncü Dereceden",
+  "IN_OUT_QUART_EASING_TYPE": "İçeri Dışarı Dördüncü Dereceden",
+  "IN_QUINT_EASING_TYPE": "İçeri Beşinci Dereceden",
+  "OUT_QUINT_EASING_TYPE": "Dışarı Beşinci Dereceden",
+  "IN_OUT_QUINT_EASING_TYPE": "İçeri Dışarı Beşinci Dereceden",
+  "IN_EXPO_EASING_TYPE": "İçeri Üstel",
+  "OUT_EXPO_EASING_TYPE": "Dışarı Üstel",
+  "IN_OUT_EXPO_EASING_TYPE": "İçeri Dışarı Üstel",
+  "IN_CIRC_EASING_TYPE": "İçeri Dairesel",
+  "OUT_CIRC_EASING_TYPE": "Dışarı Dairesel",
+  "IN_OUT_CIRC_EASING_TYPE": "İçeri Dışarı Dairesel",
+  "IN_BACK_EASING_TYPE": "İçeri Geri",
+  "OUT_BACK_EASING_TYPE": "Dışarı Geri",
+  "IN_OUT_BACK_EASING_TYPE": "İçeri Dışarı Geri",
+  "IN_ELASTIC_EASING_TYPE": "İçeri Elastik",
+  "OUT_ELASTIC_EASING_TYPE": "Dışarı Elastik",
+  "IN_OUT_ELASTIC_EASING_TYPE": "İçeri Dışarı Elastik",
+  "IN_BOUNCE_EASING_TYPE": "İçeri Sıçrama",
+  "OUT_BOUNCE_EASING_TYPE": "Dışarı Sıçrama",
+  "IN_OUT_BOUNCE_EASING_TYPE": "İçeri Dışarı Sıçrama",
+  "CLAMP_SHADER_TILE_NODE": "Sıkıştır",
+  "REPEAT_SHADER_TILE_NODE": "Tekrarla",
+  "MIRROR_SHADER_TILE_NODE": "Ayna",
+  "DECAL_SHADER_TILE_NODE": "Çıkartma",
+  "R_G_B_COMBINE_SEPARATE_COLOR_MODE": "RGB",
+  "H_S_V_COMBINE_SEPARATE_COLOR_MODE": "HSV",
+  "H_S_L_COMBINE_SEPARATE_COLOR_MODE": "HSL",
+  "COLOR_MANAGED_COLOR_SAMPLE_MODE": "Renk Yönetimli",
+  "RAW_COLOR_SAMPLE_MODE": "Ham",
+  "FRACTAL_PERLIN_NOISE_TYPE": "Perlin",
+  "TURBULENCE_PERLIN_NOISE_TYPE": "Türbülans",
+  "INHERIT_COLOR_SPACE_TYPE": "Miras Al",
+  "SRGB_COLOR_SPACE_TYPE": "sRGB",
+  "LINEAR_SRGB_COLOR_SPACE_TYPE": "Doğrusal sRGB",
+  "SIMPLE_OUTLINE_TYPE": "Basit",
+  "GAUSSIAN_OUTLINE_TYPE": "Gauss",
+  "PIXEL_PERFECT_OUTLINE_TYPE": "Piksel Mükemmel",
+  "DEGREES_ROTATION_TYPE": "Derece",
+  "RADIANS_ROTATION_TYPE": "Radyan",
+  "WEIGHTED_GRAYSCALE_MODE": "Ağırlıklı",
+  "AVERAGE_GRAYSCALE_MODE": "Ortalama",
+  "CUSTOM_GRAYSCALE_MODE": "Özel",
+  "CLAMP_TILE_MODE": "Sıkıştır",
+  "REPEAT_TILE_MODE": "Tekrarla",
+  "MIRROR_TILE_MODE": "Ayna",
+  "DECAL_TILE_MODE": "Çıkartma",
+  "ERR_UNKNOWN_FILE_FORMAT": "Bilinmeyen dosya biçimi",
+  "ERR_EXPORT_SIZE_INVALID": "Geçersiz dışa aktarma boyutu. Değerler 0'dan büyük olmalıdır.",
+  "ERR_UNKNOWN_IMG_FORMAT": "Bilinmeyen resim biçimi '{0}'.",
+  "ERR_FAILED_GENERATE_SPRITE_SHEET": "Spritesheet oluşturulamadı",
+  "ERR_NO_RENDERER": "Animasyon oluşturucu bulunamadı.",
+  "ERR_RENDERING_FAILED": "Oluşturma başarısız oldu",
+  "ENABLE_ANALYTICS": "Anonim analiz gönder",
+  "ANALYTICS_INFO": "PixiEditor'ı geliştirmek için anonim kullanım verileri topluyoruz. Hiçbir kişisel veri toplanmaz.",
+  "LANGUAGE_INFO": "Tüm çeviriler topluluk tarafından yapılmaktadır. Daha fazla bilgi için Discord sunucumuza katılın.",
+  "UP_TO_DATE_UNKNOWN": "Güncellemeler kontrol edilemedi",
+  "UP_TO_DATE": "PixiEditor güncel",
+  "UPDATE_AVAILABLE": "{0} güncellemesi mevcut",
+  "CHECKING_UPDATES": "Güncellemeler kontrol ediliyor...",
+  "UPDATE_FAILED_DOWNLOAD": "Güncelleme indirilemedi",
+  "UPDATE_READY_TO_INSTALL": "Güncelleme hazır. {0} sürümüne geçilsin mi?",
+  "SWITCH_TO_NEW_VERSION": "Geç",
+  "DOWNLOAD_UPDATE": "İndir",
+  "DOWNLOADING_UPDATE": "Güncelleme indiriliyor...",
+  "CHECKING_FOR_UPDATES": "Güncellemeler kontrol ediliyor...",
+  "PAINT_SHAPE_SETTING": "Fırça şekli",
+  "BOOL_OPERATION_NODE": "Boole İşlemi",
+  "FIRST_SHAPE": "İlk şekil",
+  "SECOND_SHAPE": "İkinci şekil",
+  "OPERATION": "İşlem",
+  "UNION_VECTOR_PATH_OP": "Birleşim",
+  "DIFFERENCE_VECTOR_PATH_OP": "Fark",
+  "INTERSECT_VECTOR_PATH_OP": "Kesişim",
+  "XOR_VECTOR_PATH_OP": "XOR",
+  "REVERSE_DIFFERENCE_VECTOR_PATH_OP": "Ters Fark",
+  "NO_DOCUMENT_OPEN": "Burada hiçbir şey yok",
+  "EMPTY_DOCUMENT_ACTION_BTN": "Oluşturmaya başla",
+  "ONBOARDING_TITLE": "Hoş geldiniz",
+  "ONBOARDING_DESCRIPTION": "Çalışma alanınızı ayarlayalım!",
+  "ONBOARDING_SKIP_BTN": "Atla",
+  "ONBOARDING_ACTION_BTN": "Hadi başlayalım",
+  "ONB_SELECT_PRIMARY_TOOLSET": "Birincil Araç Setinizi Seçin",
+  "ONB_NEXT_BTN": "İleri",
+  "ONB_FINISH_BTN": "Bitir",
+  "ONB_BACK_BTN": "Önceki",
+  "ONB_ANALYTICS": "Anonim Analiz",
+  "ONB_ALL_SET": "Her şey hazır!",
+  "ONB_ALL_SET_BTN": "Oluşturmaya başla",
+  "ANALYTICS_INFO_DETAILED": "PixiEditor, uygulamayı geliştirmek için anonim kullanım verileri toplar. Veriler hiçbir kişisel bilgi içermez. Diğer şeylerin yanı sıra, PixiEditor şunları izler:\n- Hangi araçların nasıl kullanıldığı\n- Uygulamayı ne kadar süre kullandığınız\n- Hangi komutların kullanıldığı\n- Performans verileri\n\n Ayarlardan istediğiniz zaman analizlerden çıkabilirsiniz.",
+  "PRIVACY_POLICY": "Gizlilik Politikası",
+  "ONB_SHORTCUTS": "Kısayollarınızı Seçin",
+  "GRAPH_STATE_UNABLE_TO_CREATE_MEMBER": "Mevcut Düğüm Grafiği kurulumu, seçilenin yanında yeni bir katman oluşturulmasına izin vermiyor.",
+  "PRIMARY_TOOLSET": "Birincil Araç Seti",
+  "OPEN_ONBOARDING_WINDOW": "Alıştırma penceresini aç",
+  "USER_NOT_FOUND": "Lütfen Founder's Edition'ı satın alırken kullandığınız e-postayı girin.",
+  "SESSION_NOT_VALID": "Oturum geçerli değil, lütfen tekrar giriş yapın",
+  "SESSION_NOT_FOUND": "Oturum bulunamadı, tekrar giriş yapmayı deneyin",
+  "INTERNAL_SERVER_ERROR": "Dahili bir sunucu hatası oluştu. Lütfen daha sonra tekrar deneyin.",
+  "TOO_MANY_REQUESTS": "Çok fazla istek. {0} saniye içinde tekrar deneyin.",
+  "SESSION_EXPIRED": "Oturum süresi doldu. Lütfen tekrar giriş yapın.",
+  "CONNECTION_ERROR": "Bağlantı hatası. Lütfen internet bağlantınızı kontrol edin.",
+  "FAIL_LOAD_USER_DATA": "Kaydedilmiş kullanıcı verileri yüklenemedi",
+  "LOGOUT": "Çıkış Yap",
+  "LOGGED_IN_AS": "Merhaba",
+  "EMAIL_SENT": "E-posta gönderildi! Gelen kutunuzu kontrol edin.",
+  "RESEND_ACTIVATION": "Yeniden Gönder",
+  "INVALID_TOKEN": "Oturum geçersiz veya süresi dolmuş. Lütfen tekrar giriş yapın.",
+  "ENTER_EMAIL": "E-postanızı girin",
+  "LOGIN_LINK": "Giriş Bağlantısı Gönder",
+  "LOGIN_LINK_INFO": "Giriş yapmanız için size güvenli bir bağlantı e-postayla göndereceğiz. Şifre gerekmez.",
+  "ACCOUNT_WINDOW_TITLE": "Hesap",
+  "CONNECTION_TIMEOUT": "Bağlantı zaman aşımına uğradı. Lütfen tekrar deneyin.",
+  "OPEN_ACCOUNT_WINDOW": "Hesabı Yönet",
+  "AUTO_SCALE_BACKGROUND": "Arka planı otomatik ölçeklendir",
+  "UPDATES": "Güncellemeler",
+  "SCENE": "Sahne",
+  "CUSTOM_BACKGROUND_SCALE": "Özel arka plan ölçeği",
+  "PRIMARY_BG_COLOR": "Birincil arka plan rengi",
+  "SECONDARY_BG_COLOR": "İkincil arka plan rengi",
+  "RESET": "Sıfırla",
+  "INSTALL": "Yükle",
+  "MANAGE_ACCOUNT": "Yönet",
+  "OWNED_PRODUCTS": "Sahip Olunan İçerik",
+  "INSTALLING": "Yükleniyor",
+  "INSTALLED": "Yüklendi",
+  "ACCOUNT_PROVIDER_INFO": "Hesap şunun tarafından yönetiliyor",
+  "UPDATE": "Güncelle",
+  "AUTOSAVE_OPEN_FOLDER": "Otomatik kaydetme klasörünü aç",
+  "AUTOSAVE_OPEN_FOLDER_DESCRIPTIVE": "Otomatik kayıtların saklandığı klasörü aç",
+  "AUTOSAVE_TOGGLE_DESCRIPTIVE": "Otomatik kaydetmeyi etkinleştir/devre dışı bırak",
+  "FOUNDERS_BUNDLE": "Kurucu Paketi",
+  "FOUNDERS_BUNDLE_SUBTEXT": "PixiEditor'ı destekleyin ve üretkenliğinizi artırın!",
+  "BECOME_A_FOUNDER": "Kurucu Ol",
+  "LOGIN": "Giriş Yap",
+  "NOT_FOUNDER_YET": "Henüz Kurucu değil misiniz?",
+  "ERROR_GRAPH": "Grafik kurulumu bir hata üretti. Düğüm grafiğinde düzeltin",
+  "COLOR_MATRIX_FILTER_NODE": "Renk Matris Filtresi",
+  "WORKSPACE": "Çalışma Alanı",
+  "EXPORT_ZONE_NODE": "Dışa Aktarma Bölgesi",
+  "IS_DEFAULT_EXPORT": "Varsayılan Dışa Aktarma mı",
+  "EXPORT_OUTPUT": "Dışa Aktarma Çıktısı",
+  "RENDER_OUTPUT_SIZE": "Oluşturma Çıktı Boyutu",
+  "RENDER_OUTPUT_CENTER": "Oluşturma Çıktı Merkezi",
+  "COLOR_PICKER": "Renk Seçici",
+  "UNAUTHORIZED_ACCESS": "Yetkisiz erişim",
+  "SEPARATE_SHAPES": "Şekilleri Ayır",
+  "SEPARATE_SHAPES_DESCRIPTIVE": "Mevcut vektörden şekilleri ayrı katmanlara ayır",
+  "TEXT": "Metin",
+  "EXTRACT_SELECTED_TEXT": "Seçili metni çıkar",
+  "EXTRACT_SELECTED_TEXT_DESCRIPTIVE": "Seçili metni yeni katmana çıkar.",
+  "EXTRACT_SELECTED_CHARACTERS": "Seçili karakterleri çıkar",
+  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Seçimden bireysel karakterleri yeni katmanlara çıkar.",
+  "STEP_START": "En yakın kareye geri adım at",
+  "STEP_END": "En yakın kareye ileri adım at",
+  "STEP_FORWARD": "Bir kare ileri adım at",
+  "STEP_BACK": "Bir kare geri adım at",
+  "ANIMATION_QUALITY_PRESET": "Kalite Önayarı",
+  "VERY_LOW_QUALITY_PRESET": "Çok Düşük",
+  "LOW_QUALITY_PRESET": "Düşük",
+  "MEDIUM_QUALITY_PRESET": "Orta",
+  "HIGH_QUALITY_PRESET": "Yüksek",
+  "VERY_HIGH_QUALITY_PRESET": "Çok Yüksek",
+  "EXPORT_FRAMES": "Kareleri Dışa Aktar",
+  "NORMALIZE_OFFSET": "Ofseti Normalleştir",
+  "TANGENT": "Teğet",
+  "EVALUATE_PATH_NODE": "Yolu Değerlendir",
+  "OLD_MIN": "Eski Min",
+  "OLD_MAX": "Eski Maks",
+  "NEW_MIN": "Yeni Min",
+  "NEW_MAX": "Yeni Maks",
+  "REMAP_NODE": "Yeniden Haritala",
+  "TEXT_TOOL_ACTION_DISPLAY": "Yeni bir metin eklemek için tuvale tıklayın (boyutu ayarlamak için tıklarken sürükleyin). Düzenlemek için mevcut metne tıklayın.",
+  "PASTE_CELS": "Kareleri yapıştır",
+  "SCALE_X": "Ölçek X",
+  "SCALE_Y": "Ölçek Y",
+  "TRANSLATE_X": "Öteleme X",
+  "TRANSLATE_Y": "Öteleme Y",
+  "SKEW_X": "Eğme X",
+  "SKEW_Y": "Eğme Y",
+  "PERSPECTIVE_0": "Perspektif 0",
+  "PERSPECTIVE_1": "Perspektif 1",
+  "PERSPECTIVE_2": "Perspektif 2",
+  "COMPOSE_MATRIX": "Matris Oluştur",
+  "DECOMPOSE_MATRIX": "Matrisi Ayrıştır",
+  "NORMALIZE_COORDINATES": "Koordinatları Normalleştir",
+  "TRANSFORMED_POSITION": "Dönüştürülmüş Konum",
+  "ACCOUNT_PROVIDER_NOT_AVAILABLE": "PixiEditor'ın bu yapısı hesapları desteklemiyor. Hesabınızı yönetmek için pixieditor.net'teki resmi yapıyı kullanın.",
+  "STEAM_OFFLINE": "Hesap doğrulanamıyor. Steam çevrimdışı. Steam istemcisinin çalıştığından ve giriş yaptığınızdan emin olun.",
+  "ERROR_GPU_RESOURCES_CREATION": "Kaynaklar oluşturulamadı: GPU sürücülerinizi güncellemeyi deneyin veya ayarlarda farklı bir oluşturma API'si ayarlamayı deneyin. \nHata: '{0}'",
+  "ERROR_SAVING_PREFERENCES_DESC": "Tercihler şu hatayla kaydedilemedi: '{0}'. Lütfen PixiEditor veri klasörüne yazma izniniz olup olmadığını kontrol edin.",
+  "ERROR_SAVING_PREFERENCES": "Tercihler kaydedilemedi",
+  "PREFERRED_RENDERER": "Tercih Edilen Oluşturma Api"
+}

+ 297 - 3
src/PixiEditor/Data/Localization/Languages/uk.json

@@ -667,8 +667,6 @@
   "MODIFY_IMAGE_PAIR_NODE": "Змінити зображення",
   "WITHOUT_FILTERS": "Без фільтрів",
   "RAW_LAYER_OUTPUT": "Raw",
-  "BETA_EXAMPLE_FILES": "Файли прикладів бета-версії",
-  "BETA_PROCEDURAL_GENERATION": "Процедурна анімація",
   "POND_EXAMPLE": "Ставок",
   "TREE_EXAMPLE": "Вітряне дерево",
   "OUTLINE_EXAMPLE": "Автоматичний контур",
@@ -827,5 +825,301 @@
   "PRESERVE_ALPHA": "Зберегти альфа-канал",
   "BLUR_FILTER_NODE": "Фільтр розмиття Гауса",
   "LENGTH": "Довжина",
-  "GREATER_THAN_OR_EQUAL": "Більше або дорівнює"
+  "GREATER_THAN_OR_EQUAL": "Більше або дорівнює",
+  "WEBP_FILE": "Зображення WebP",
+  "COLOR_NODE": "Колір",
+  "CONVERT_TO_CURVE": "Перетворити на криву",
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Перетворити вибраний векторний шар на криву/шлях",
+  "FONT_FILES": "Файли шрифтів",
+  "UNIT_PT": "пт",
+  "FONT_LABEL": "Сімейство",
+  "FONT_SIZE_LABEL": "Розмір",
+  "SPACING_LABEL": "Інтервал",
+  "TEXT_TOOL": "Текст",
+  "MISSING_FONT": "Відсутній шрифт",
+  "TEXT_LAYER_NAME": "Текст",
+  "TEXT_TOOL_TOOLTIP": "Створити текст ({0}).",
+  "BOLD_TOOLTIP": "Жирний",
+  "ITALIC_TOOLTIP": "Курсив",
+  "CUSTOM_FONT": "Користувацький шрифт",
+  "DUMP_GPU_DIAGNOSTICS": "Дамп діагностики GPU",
+  "USE_SRGB_PROCESSING": "Використовувати sRGB для обробки кольорів",
+  "USE_SRGB_PROCESSING_DESC": "Перетворити документ, використовуючи лінійний sRGB на sRGB для обробки кольорів. Це вплине на кольори документа.",
+  "TEXT_NODE": "Текст",
+  "TEXT_LABEL": "Текст",
+  "TEXT_ON_PATH_NODE": "Текст на шляху",
+  "HIGH_DPI_RENDERING": "Рендеринг з високою роздільною здатністю",
+  "THICKNESS": "Товщина",
+  "TYPE": "Тип",
+  "EFFECTS": "Ефекти",
+  "OUTLINE_NODE": "Контур",
+  "SHADER_CODE": "Код шейдера",
+  "SHADER_NODE": "Шейдер",
+  "FAILED_TO_OPEN_EDITABLE_STRING_TITLE": "Не вдалося відкрити файл",
+  "FAILED_TO_OPEN_EDITABLE_STRING_MESSAGE": "Не вдалося редагувати цей рядок у зовнішньому редакторі. Причина: {0}",
+  "STRING_EDIT_IN_DEFAULT_APP": "Редагувати у програмі за замовчуванням",
+  "STRING_OPEN_IN_FOLDER": "Відкрити у папці",
+  "DISCO_BALL_EXAMPLE": "Диско-куля",
+  "COLOR_SPACE": "Кольоровий простір",
+  "PHOTO_EXAMPLES": "Фото",
+  "MASK_EXAMPLE": "Маска",
+  "SHADOW_NODE": "Фільтр тіней",
+  "INPUT_MATRIX": "Вхідна матриця",
+  "OUTPUT_MATRIX": "Вихідна матриця",
+  "CENTER": "Центр",
+  "CONTENT_OFFSET": "Зсув вмісту",
+  "CANVAS_POSITION": "Положення полотна",
+  "CENTER_POSITION": "Положення центру",
+  "TILE_MODE_X": "Режим плитки X",
+  "TILE_MODE_Y": "Режим плитки Y",
+  "TILE_NODE": "Плитка",
+  "SKEW": "Нахил",
+  "OFFSET_NODE": "Зсув",
+  "SKEW_NODE": "Нахил",
+  "ROTATION_NODE": "Обертання",
+  "SCALE_NODE": "Масштаб",
+  "ROTATE_NODE": "Обертати",
+  "TRANSFORM_NODE": "Трансформація",
+  "UNIT": "Одиниця",
+  "ANGLE": "Кут",
+  "DOCUMENT_INFO_NODE": "Інформація про документ",
+  "MASK_NODE": "Маска",
+  "SEPIA_FILTER_NODE": "Фільтр сепії",
+  "INTENSITY": "Інтенсивність",
+  "INVERT_FILTER_NODE": "Інвертувати фільтр",
+  "COLOR_ADJUSTMENTS_FILTER": "Фільтр налаштувань кольору",
+  "ADJUST_BRIGHTNESS": "Налаштувати яскравість",
+  "ADJUST_CONTRAST": "Налаштувати контрастність",
+  "ADJUST_SATURATION": "Налаштувати насиченість",
+  "ADJUST_TEMPERATURE": "Налаштувати температуру",
+  "ADJUST_TINT": "Налаштувати відтінок",
+  "ADJUST_HUE": "Налаштувати тон",
+  "HUE_VALUE": "Тон",
+  "SATURATION_VALUE": "Насиченість",
+  "BRIGHTNESS_VALUE": "Яскравість",
+  "CONTRAST_VALUE": "Контрастність",
+  "TEMPERATURE_VALUE": "Температура",
+  "TINT_VALUE": "Тон",
+  "FAILED_DOWNLOADING_UPDATE_TITLE": "Не вдалося завантажити оновлення",
+  "FAILED_DOWNLOADING_UPDATE": "Не вдалося завантажити оновлення. Спробуйте ще раз пізніше.",
+  "DELETE_SELECTED": "Видалити вибране",
+  "DELETE_SELECTED_DESCRIPTIVE": "Видалити вибраний елемент (шар, пікселі тощо.)",
+  "GRIDLINES_SIZE": "Розмір сітки",
+  "CANVAS": "Полотно",
+  "UNEXPECTED_SHUTDOWN": "Несподіване завершення роботи",
+  "UNEXPECTED_SHUTDOWN_MSG": "PixiEditor несподівано завершив роботу. Ми завантажили останнє автозбереження ваших файлів.",
+  "OK": "Добре",
+  "OPEN_AUTOSAVES": "Переглянути автозбереження",
+  "AUTOSAVE_SETTINGS_HEADER": "Автозбереження",
+  "AUTOSAVE_SETTINGS_SAVE_STATE": "Відкрити останні файли під час запуску",
+  "AUTOSAVE_SETTINGS_PERIOD": "Період автозбереження",
+  "AUTOSAVE_ENABLED": "Автозбереження ввімкнено",
+  "MINUTE_UNIVERSAL": "хв",
+  "AUTOSAVE_SETTINGS_SAVE_USER_FILE": "Автозбереження у вибраний файл",
+  "LOAD_LAZY_FILE_MESSAGE": "Щоб покращити час запуску, PixiEditor не завантажив цей файл. Натисніть кнопку нижче, щоб завантажити його.",
+  "EASING_NODE": "Послаблення",
+  "EASING_TYPE": "Тип послаблення",
+  "OPEN_DIRECTORY_ON_EXPORT": "Відкрити каталог під час експорту",
+  "ERROR_LOOP_DETECTED_MESSAGE": "Переміщення цього шару створить цикл. Виправте це в графі вузлів.",
+  "LINEAR_EASING_TYPE": "Лінійний",
+  "IN_SINE_EASING_TYPE": "Вхідний синусоїдний",
+  "OUT_SINE_EASING_TYPE": "Вихідний синусоїдний",
+  "IN_OUT_SINE_EASING_TYPE": "Вхідний вихідний синусоїдний",
+  "IN_QUAD_EASING_TYPE": "У чотирикутному",
+  "OUT_QUAD_EASING_TYPE": "Квадратичний вихід",
+  "IN_OUT_QUAD_EASING_TYPE": "Квадратичні вхід та вихід",
+  "IN_CUBIC_EASING_TYPE": "Кубічний вхід",
+  "OUT_CUBIC_EASING_TYPE": "Кубічний вихід",
+  "IN_OUT_CUBIC_EASING_TYPE": "Кубічні вхід і вихід",
+  "IN_QUART_EASING_TYPE": "In Quart",
+  "OUT_QUART_EASING_TYPE": "Out Quart",
+  "IN_OUT_QUART_EASING_TYPE": "In Out Quart",
+  "IN_QUINT_EASING_TYPE": "In Quint",
+  "OUT_QUINT_EASING_TYPE": "In Quint",
+  "IN_OUT_QUINT_EASING_TYPE": "In Quint",
+  "IN_EXPO_EASING_TYPE": "In Expo",
+  "OUT_EXPO_EASING_TYPE": "Out Expo",
+  "IN_OUT_EXPO_EASING_TYPE": "In Out Expo",
+  "IN_CIRC_EASING_TYPE": "In Circ",
+  "OUT_CIRC_EASING_TYPE": "Out Circ",
+  "IN_OUT_CIRC_EASING_TYPE": "In Out Circ",
+  "IN_BACK_EASING_TYPE": "In Back",
+  "OUT_BACK_EASING_TYPE": "Out Back",
+  "IN_OUT_BACK_EASING_TYPE": "In Out Back",
+  "IN_ELASTIC_EASING_TYPE": "Еластичний вхід",
+  "OUT_ELASTIC_EASING_TYPE": "Еластичний вихід",
+  "IN_OUT_ELASTIC_EASING_TYPE": "Еластичні вхід і вихід",
+  "IN_BOUNCE_EASING_TYPE": "Відскок на вході",
+  "OUT_BOUNCE_EASING_TYPE": "Відскок на виході",
+  "IN_OUT_BOUNCE_EASING_TYPE": "Відскок на вході та виході",
+  "CLAMP_SHADER_TILE_NODE": "Затиснути",
+  "REPEAT_SHADER_TILE_NODE": "Повторити",
+  "MIRROR_SHADER_TILE_NODE": "Дзеркало",
+  "DECAL_SHADER_TILE_NODE": "Декаль",
+  "R_G_B_COMBINE_SEPARATE_COLOR_MODE": "RGB",
+  "H_S_V_COMBINE_SEPARATE_COLOR_MODE": "HSV",
+  "H_S_L_COMBINE_SEPARATE_COLOR_MODE": "HSL",
+  "COLOR_MANAGED_COLOR_SAMPLE_MODE": "Керований кольором",
+  "RAW_COLOR_SAMPLE_MODE": "Необроблений",
+  "FRACTAL_PERLIN_NOISE_TYPE": "Шум Перліна",
+  "TURBULENCE_PERLIN_NOISE_TYPE": "Турбулентність",
+  "INHERIT_COLOR_SPACE_TYPE": "Успадкування",
+  "SRGB_COLOR_SPACE_TYPE": "sRGB",
+  "LINEAR_SRGB_COLOR_SPACE_TYPE": "Лінійний sRGB",
+  "SIMPLE_OUTLINE_TYPE": "Простий",
+  "GAUSSIAN_OUTLINE_TYPE": "Гаусівський",
+  "PIXEL_PERFECT_OUTLINE_TYPE": "Піксельно ідеальний",
+  "DEGREES_ROTATION_TYPE": "Градуси",
+  "RADIANS_ROTATION_TYPE": "Радіани",
+  "WEIGHTED_GRAYSCALE_MODE": "Зважений",
+  "AVERAGE_GRAYSCALE_MODE": "Усереднений",
+  "CUSTOM_GRAYSCALE_MODE": "Налаштований",
+  "CLAMP_TILE_MODE": "Затискач",
+  "REPEAT_TILE_MODE": "Повторити",
+  "MIRROR_TILE_MODE": "Дзеркало",
+  "DECAL_TILE_MODE": "Наклейка",
+  "ERR_UNKNOWN_FILE_FORMAT": "Невідомий формат файлу",
+  "ERR_EXPORT_SIZE_INVALID": "Недійсний розмір експорту. Значення мають бути більшими за 0.",
+  "ERR_UNKNOWN_IMG_FORMAT": "Невідомий формат зображення '{0}'.",
+  "ERR_FAILED_GENERATE_SPRITE_SHEET": "Не вдалося створити спрайт-лист",
+  "ERR_NO_RENDERER": "Рендер анімації не знайдено.",
+  "ERR_RENDERING_FAILED": "Рендеринг не вдався",
+  "ENABLE_ANALYTICS": "Надіслати анонімну аналітику",
+  "ANALYTICS_INFO": "Ми збираємо анонімні дані про використання для покращення PixiEditor. Персональні дані не збираються.",
+  "LANGUAGE_INFO": "Усі переклади здійснюються спільнотою. Приєднуйтесь до нашого сервера Discord для отримання додаткової інформації.",
+  "UP_TO_DATE_UNKNOWN": "Не вдалося перевірити наявність оновлень",
+  "UP_TO_DATE": "PixiEditor оновлено",
+  "UPDATE_AVAILABLE": "Доступне оновлення: {0}",
+  "CHECKING_UPDATES": "Перевірка оновлень...",
+  "UPDATE_FAILED_DOWNLOAD": "Не вдалося завантажити оновлення",
+  "UPDATE_READY_TO_INSTALL": "Оновлення готове. Перейти на {0}?",
+  "SWITCH_TO_NEW_VERSION": "Перейти",
+  "DOWNLOAD_UPDATE": "Завантажити",
+  "CHECKING_FOR_UPDATES": "Перевірка оновлень...",
+  "PAINT_SHAPE_SETTING": "Форма пензля",
+  "BOOL_OPERATION_NODE": "Дії з булевою логікою",
+  "FIRST_SHAPE": "Перша фігура",
+  "SECOND_SHAPE": "Друга фігура",
+  "OPERATION": "Дія",
+  "UNION_VECTOR_PATH_OP": "Об'єднання",
+  "DIFFERENCE_VECTOR_PATH_OP": "Різниця",
+  "INTERSECT_VECTOR_PATH_OP": "Перетин",
+  "XOR_VECTOR_PATH_OP": "XOR",
+  "REVERSE_DIFFERENCE_VECTOR_PATH_OP": "Зворотна різниця",
+  "NO_DOCUMENT_OPEN": "Тут нічого немає",
+  "EMPTY_DOCUMENT_ACTION_BTN": "Почати творити",
+  "ONBOARDING_TITLE": "Ласкаво просимо до",
+  "ONBOARDING_DESCRIPTION": "Давайте налаштуємо ваш робочий простір!",
+  "ONBOARDING_SKIP_BTN": "Пропустити",
+  "ONBOARDING_ACTION_BTN": "Почнемо",
+  "ONB_SELECT_PRIMARY_TOOLSET": "Виберіть свій основний набір інструментів",
+  "ONB_NEXT_BTN": "Далі",
+  "ONB_BACK_BTN": "Назад",
+  "ONB_ANALYTICS": "Анонімна аналітика",
+  "ONB_ALL_SET": "Ви готові!",
+  "ONB_ALL_SET_BTN": "Почати творити",
+  "ANALYTICS_INFO_DETAILED": "PixiEditor збирає анонімні дані про використання для покращення програми. Дані не містять жодної особистої інформації. Серед іншого, PixiEditor відстежує наступне:\n- Які інструменти використовуються і як\n- Як довго ви використовуєте програму\n- Які команди використовуються\n- Дані про продуктивність\n\nВи можете будь-коли відмовитися від аналітики в налаштуваннях.",
+  "PRIVACY_POLICY": "Політика конфіденційності",
+  "ONB_SHORTCUTS": "Виберіть свої комбінації клавіш",
+  "GRAPH_STATE_UNABLE_TO_CREATE_MEMBER": "Поточні налаштування графу вузлів забороняють створення нового шару поруч із вибраним.",
+  "PRIMARY_TOOLSET": "Основний набір інструментів",
+  "OPEN_ONBOARDING_WINDOW": "Відкрити вікно адаптації",
+  "AUTO_SCALE_BACKGROUND": "Автоматичне масштабування фону",
+  "UPDATES": "Оновлення",
+  "SCENE": "Сцена",
+  "CUSTOM_BACKGROUND_SCALE": "Налаштування масштабу фону",
+  "PRIMARY_BG_COLOR": "Основний колір фону",
+  "SECONDARY_BG_COLOR": "Додатковий колір фону",
+  "RESET": "Скинути",
+  "AUTOSAVE_OPEN_FOLDER": "Відкрити папку автозбереження",
+  "AUTOSAVE_OPEN_FOLDER_DESCRIPTIVE": "Відкрити папку, де зберігаються автозбереження",
+  "AUTOSAVE_TOGGLE_DESCRIPTIVE": "Увімкнути/вимкнути автозбереження",
+  "OPEN_TYPE_FONT": "Шрифти OpenType",
+  "TRUE_TYPE_FONT": "Шрифти TrueType",
+  "SVG_FILE": "Масштабована векторна графіка",
+  "EXAMPLE_FILES": "Приклади файлів",
+  "PROCEDURAL_GENERATION": "Процедурна анімація",
+  "ERROR_GRAPH": "Налаштування графу призвело до помилки. Виправити це в графі вузлів",
+  "COLOR_MATRIX_FILTER_NODE": "Фільтр кольорової матриці",
+  "WORKSPACE": "Робочий простір",
+  "EXPORT_ZONE_NODE": "Зона експорту",
+  "IS_DEFAULT_EXPORT": "Експорт за замовчуванням",
+  "EXPORT_OUTPUT": "Вивід експорту",
+  "RENDER_OUTPUT_SIZE": "Розмір виводу рендерингу",
+  "RENDER_OUTPUT_CENTER": "Центр виводу візуалізації",
+  "COLOR_PICKER": "Вибір кольору",
+  "UNAUTHORIZED_ACCESS": "Несанкціонований доступ",
+  "SEPARATE_SHAPES": "Розділити фігури",
+  "SEPARATE_SHAPES_DESCRIPTIVE": "Розділити фігури з поточного вектора на окремі шари",
+  "TEXT": "Текст",
+  "EXTRACT_SELECTED_TEXT": "Вилучити виділений текст",
+  "EXTRACT_SELECTED_TEXT_DESCRIPTIVE": "Вилучити виділений текст у новий шар.",
+  "EXTRACT_SELECTED_CHARACTERS": "Вилучити виділені символи",
+  "EXTRACT_SELECTED_CHARACTERS_DESCRIPTIVE": "Вилучити окремі символи з виділення у нові шари.",
+  "STEP_START": "Крок назад до найближчої комірки",
+  "STEP_END": "Крок вперед до найближчої комірки",
+  "STEP_FORWARD": "На один кадр вперед",
+  "STEP_BACK": "На один кадр назад",
+  "ONB_FINISH_BTN": "Завершити",
+  "USER_NOT_FOUND": "Будь ласка, введіть електронну адресу, яку ви використовували для придбання видання Founder's Edition.",
+  "SESSION_NOT_VALID": "Сеанс недійсний, будь ласка, увійдіть ще раз",
+  "SESSION_NOT_FOUND": "Сеанс не знайдено, спробуйте увійти ще раз",
+  "INTERNAL_SERVER_ERROR": "Сталася внутрішня помилка сервера. Будь ласка, спробуйте ще раз пізніше.",
+  "TOO_MANY_REQUESTS": "Забагато запитів. Спробуйте ще раз через {0} секунд.",
+  "SESSION_EXPIRED": "Сеанс минув. Будь ласка, увійдіть знову.",
+  "CONNECTION_ERROR": "Помилка підключення. Будь ласка, перевірте ваше інтернет-з’єднання.",
+  "FAIL_LOAD_USER_DATA": "Не вдалося завантажити збережені дані користувача",
+  "LOGOUT": "Вийти",
+  "LOGGED_IN_AS": "Вітаємо",
+  "EMAIL_SENT": "Електронний лист надіслано! Перевірте свою поштову скриньку.",
+  "RESEND_ACTIVATION": "Надіслати повторно",
+  "INVALID_TOKEN": "Сеанс недійсний або минув. Будь ласка, увійдіть знову.",
+  "ENTER_EMAIL": "Введіть свою електронну адресу",
+  "LOGIN_LINK": "Надіслати посилання для входу",
+  "LOGIN_LINK_INFO": "Ми надішлемо вам електронною поштою безпечне посилання для входу. Пароль не потрібен.",
+  "ACCOUNT_WINDOW_TITLE": "Обліковий запис",
+  "CONNECTION_TIMEOUT": "Час очікування з’єднання минув. Будь ласка, спробуйте ще раз.",
+  "OPEN_ACCOUNT_WINDOW": "Керування обліковим записом",
+  "INSTALL": "Встановити",
+  "MANAGE_ACCOUNT": "Керування",
+  "OWNED_PRODUCTS": "Власний контент",
+  "INSTALLING": "Встановлення",
+  "INSTALLED": "Встановлено",
+  "ACCOUNT_PROVIDER_INFO": "Обліковий запис належить",
+  "UPDATE": "Оновлення",
+  "FOUNDERS_BUNDLE": "Набір засновника",
+  "FOUNDERS_BUNDLE_SUBTEXT": "Підтримуйте PixiEditor та підвищуйте свою продуктивність!",
+  "BECOME_A_FOUNDER": "Стати засновником",
+  "LOGIN": "Увійти",
+  "NOT_FOUNDER_YET": "Ще не засновник?",
+  "ANIMATION_QUALITY_PRESET": "Шаблон якості",
+  "VERY_LOW_QUALITY_PRESET": "Дуже низько",
+  "LOW_QUALITY_PRESET": "Низько",
+  "MEDIUM_QUALITY_PRESET": "Середньо",
+  "HIGH_QUALITY_PRESET": "Високо",
+  "VERY_HIGH_QUALITY_PRESET": "Дуже високо",
+  "EXPORT_FRAMES": "Експорт кадрів",
+  "NORMALIZE_OFFSET": "Зсув нормалізації",
+  "TANGENT": "Дотична",
+  "EVALUATE_PATH_NODE": "Оцінка шляху",
+  "OLD_MIN": "Стар. мін.",
+  "OLD_MAX": "Стар. макс.",
+  "NEW_MIN": "Нов. мін",
+  "NEW_MAX": "Нов. макс",
+  "REMAP_NODE": "Перепризначення",
+  "TEXT_TOOL_ACTION_DISPLAY": "Клацніть на полотні, щоб додати новий текст (перетягніть під час клацання, щоб встановити розмір). Клацніть на існуючому тексті, щоб редагувати його.",
+  "PASTE_CELS": "Вставити комірки",
+  "SCALE_X": "Масштаб X",
+  "SCALE_Y": "Масштаб Y",
+  "TRANSLATE_X": "Трансляція X",
+  "TRANSLATE_Y": "Трансляція Y",
+  "SKEW_X": "Перекіс X",
+  "SKEW_Y": "Перекіс Y",
+  "PERSPECTIVE_0": "Перспектива 0",
+  "PERSPECTIVE_1": "Перспектива 1",
+  "PERSPECTIVE_2": "Перспектива 2",
+  "COMPOSE_MATRIX": "Скласти матрицю",
+  "DECOMPOSE_MATRIX": "Розкласти матрицю",
+  "NORMALIZE_COORDINATES": "Нормаліз. координати",
+  "TRANSFORMED_POSITION": "Перетворена позиція"
 }

+ 14 - 7
src/PixiEditor/Data/Localization/LocalizationData.json

@@ -13,14 +13,14 @@
       "code": "pl",
       "localeFileName": "pl.json",
       "iconFileName": "pl.png",
-      "lastUpdated": "2025-01-10 16:27:36"
+      "lastUpdated": "2025-07-28 07:08:12"
     },
     {
       "name": "Deutsch",
       "code": "de",
       "localeFileName": "de.json",
       "iconFileName": "de.png",
-      "lastUpdated": "2025-01-10 16:14:53"
+      "lastUpdated": "2025-07-24 17:51:17"
     },
     {
       "name": "Español",
@@ -42,14 +42,14 @@
       "code": "ru",
       "localeFileName": "ru.json",
       "iconFileName": "ru.png",
-      "lastUpdated": "2025-03-24 19:03:12"
+      "lastUpdated": "2025-06-30 11:13:26"
     },
     {
       "name": "Українська",
       "code": "uk",
       "localeFileName": "uk.json",
       "iconFileName": "uk.png",
-      "lastUpdated": "2025-01-10 17:55:45"
+      "lastUpdated": "2025-06-28 12:26:13"
     },
     {
       "name": "عربي",
@@ -57,14 +57,14 @@
       "localeFileName": "ar.json",
       "iconFileName": "ar.png",
       "rightToLeft": true,
-      "lastUpdated": "2024-07-25 16:37:09"
+      "lastUpdated": "2025-06-04 18:20:46"
     },
     {
       "name": "Čeština",
       "code": "cs",
       "localeFileName": "cs.json",
       "iconFileName": "cs.png",
-      "lastUpdated": "2025-02-11 21:40:35"
+      "lastUpdated": "2025-07-25 12:03:22"
     },
     {
       "name": "Português (Brasil)",
@@ -79,6 +79,13 @@
       "localeFileName": "hu.json",
       "iconFileName": "hu.png",
       "lastUpdated": "2023-05-08 22:07:37"
+    },
+    {
+      "name": "Türkçe",
+      "code": "tr",
+      "localeFileName": "tr.json",
+      "iconFileName": "tr.png",
+      "lastUpdated": "2025-08-06 09:23:12"
     }
   ]
-}
+}

+ 2 - 2
src/PixiEditor/Helpers/Constants/ClipboardDataFormats.cs

@@ -2,12 +2,12 @@
 
 public static class ClipboardDataFormats
 {
-    public const string Png = "PNG";
+    public static readonly string[] PngFormats = [ "PNG", "image/png", "public.png" ];
     public const string LayerIdList = "PixiEditor.LayerIdList";
     public const string PositionFormat = "PixiEditor.Position";
-    public const string ImageSlashPng = "image/png";
     public const string DocumentFormat = "PixiEditor.Document";
     public const string NodeIdList = "PixiEditor.NodeIdList";
     public const string CelIdList = "PixiEditor.CelIdList";
     public const string PixiVectorData = "PixiEditor.VectorData";
+    public const string UriList = "text/uri-list";
 }

+ 2 - 1
src/PixiEditor/Helpers/Converters/FormattedColorConverter.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.Globalization;
 using Avalonia.Media;
+using PixiEditor.Helpers.Extensions;
 
 namespace PixiEditor.Helpers.Converters;
 
@@ -24,7 +25,7 @@ internal class FormattedColorConverter
 
         return format.ToLowerInvariant() switch
         {
-            "hex" => color.ToString(),
+            "hex" => color.ToColor().ToRgbHex(),
             "rgba" => $"({color.R}, {color.G}, {color.B}, {color.A})",
             _ => "",
         };

+ 7 - 2
src/PixiEditor/Helpers/Converters/SocketColorConverter.cs

@@ -6,8 +6,13 @@ namespace PixiEditor.Helpers.Converters;
 
 internal class SocketColorConverter : SingleInstanceConverter<SocketColorConverter>
 {
-    Color unknownColor = Color.FromRgb(255, 0, 255);
+    static Color unknownColor = Color.FromRgb(255, 0, 255);
     public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        return SocketToColor(value);
+    }
+
+    public static Color SocketToColor(object value)
     {
         if (value is IBrush brush)
         {
@@ -16,7 +21,7 @@ internal class SocketColorConverter : SingleInstanceConverter<SocketColorConvert
             if (brush is GradientBrush linearGradientBrush)
                 return linearGradientBrush.GradientStops.FirstOrDefault()?.Color ?? unknownColor;
         }
-        
+
         return unknownColor;
     }
 }

+ 2 - 2
src/PixiEditor/Helpers/Extensions/ColorHelpers.cs

@@ -92,8 +92,8 @@ internal static class ColorHelpers
         RadialGradientPaintable radialGradientPaintable => new RadialGradientBrush
         {
             Center = new RelativePoint(radialGradientPaintable.Center.X, radialGradientPaintable.Center.Y, paintable.AbsoluteValues ? RelativeUnit.Absolute : RelativeUnit.Relative),
-            RadiusX = new RelativeScalar(radialGradientPaintable.Radius, RelativeUnit.Absolute),
-            RadiusY = new RelativeScalar(radialGradientPaintable.Radius, RelativeUnit.Absolute),
+            RadiusX = new RelativeScalar(radialGradientPaintable.Radius, paintable.AbsoluteValues ? RelativeUnit.Absolute : RelativeUnit.Relative),
+            RadiusY = new RelativeScalar(radialGradientPaintable.Radius, paintable.AbsoluteValues ? RelativeUnit.Absolute : RelativeUnit.Relative),
             GradientStops = ToAvaloniaGradientStops(radialGradientPaintable.GradientStops),
             Transform = radialGradientPaintable.Transform.HasValue ? new MatrixTransform(ToAvaloniaMatrix(radialGradientPaintable.Transform.Value)) : null
         },

+ 13 - 5
src/PixiEditor/Helpers/Extensions/DataObjectExtensions.cs

@@ -37,14 +37,22 @@ public static class DataObjectExtensions
             return false;
         }
 
-        string text = data.GetText();
-
-        if (Directory.Exists(text) || File.Exists(text))
+        try
+        {
+            var text = data.GetText();
+            if (Directory.Exists(text) || File.Exists(text))
+            {
+                path = text;
+                return true;
+            }
+        }
+        catch(InvalidCastException ex) // bug on x11
         {
-            path = text;
-            return true;
+            path = null;
+            return false;
         }
 
+
         path = null;
         return false;
     }

+ 135 - 1
src/PixiEditor/Helpers/Extensions/EnumerableExtensions.cs

@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System.Buffers;
+using System.Collections.Generic;
 
 namespace PixiEditor.Helpers.Extensions;
 
@@ -95,4 +96,137 @@ internal static class EnumerableExtensions
 
         return IndexOrNext(collection, predicate, index, false);
     }
+
+    public static T IndexOrNextInDirection<T>(this IEnumerable<T> collection, Predicate<T> predicate, int index, NextToDirection direction, bool overrun = true) => direction switch
+    {
+        NextToDirection.Forwards => IndexOrNext(collection, predicate, index, overrun),
+        NextToDirection.Backwards => IndexOrPrevious(collection, predicate, index, overrun),
+        _ => throw new ArgumentOutOfRangeException(nameof(direction)),
+    };
+    
+    /// <summary>
+    /// Returns the element that comes immediately after the specified <paramref name="index"/> 
+    /// in the given <paramref name="enumerable"/>, wrapping around to the first element if 
+    /// the end of the sequence is reached.
+    /// </summary>
+    /// <typeparam name="T">The type of the elements in the enumerable.</typeparam>
+    /// <param name="enumerable">The source enumerable.</param>
+    /// <param name="index">The index of the reference element. Must be non-negative.</param>
+    /// <returns>
+    /// The element immediately after the specified index, or the first element if the index 
+    /// refers to the last element. Returns <c>default</c> if the enumerable is empty.
+    /// </returns>
+    /// <remarks>
+    /// This method does not check whether the specified <paramref name="index"/> is within the 
+    /// bounds of the enumerable's size. Passing a positive out-of-range index may yield unexpected results.
+    /// </remarks>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="enumerable"/> is <c>null</c>.</exception>
+    /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is negative.</exception>
+    public static T? WrapNextAfterIndex<T>(this IEnumerable<T> enumerable, int index)
+    {
+        ArgumentOutOfRangeException.ThrowIfNegative(index);
+        ArgumentNullException.ThrowIfNull(enumerable);
+
+        switch (enumerable)
+        {
+            case ICollection<T> collection:
+                return NextWithKnownCount(collection, index, collection.Count);
+            case IReadOnlyCollection<T> readOnlyCollection:
+                return NextWithKnownCount(readOnlyCollection, index, readOnlyCollection.Count);
+        }
+
+        using var enumerator = enumerable.GetEnumerator();
+
+        // If the enumerable is empty, return null
+        if (!enumerator.MoveNext())
+            return default;
+
+        var steps = index + 1;
+        var firstElement = enumerator.Current;
+
+        while (steps-- > 0)
+        {
+            if (!enumerator.MoveNext())
+                return firstElement;
+        }
+        
+        return enumerator.Current;
+    }
+
+    /// <summary>
+    /// Returns the element that comes immediately before the specified <paramref name="index"/> 
+    /// in the given <paramref name="enumerable"/>, wrapping around to the last element if 
+    /// the start of the sequence is reached.
+    /// </summary>
+    /// <typeparam name="T">The type of the elements in the enumerable.</typeparam>
+    /// <param name="enumerable">The source enumerable.</param>
+    /// <param name="index">The index of the reference element. Must be non-negative.</param>
+    /// <returns>
+    /// The element immediately before the specified index, or the last element if the index 
+    /// is <c>0</c>. Returns <c>default</c> if the enumerable is empty.
+    /// </returns>
+    /// <remarks>
+    /// This method does not check whether the specified <paramref name="index"/> is within the 
+    /// bounds of the enumerable's size. Passing a positive out-of-range index may yield unexpected results.
+    /// </remarks>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="enumerable"/> is <c>null</c>.</exception>
+    /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is negative.</exception>
+    public static T? WrapPreviousBeforeIndex<T>(this IEnumerable<T> enumerable, int index)
+    {
+        ArgumentOutOfRangeException.ThrowIfNegative(index);
+        ArgumentNullException.ThrowIfNull(enumerable);
+        
+        return index == 0
+            ? enumerable.LastOrDefault()
+            : enumerable.ElementAtOrDefault(index - 1);
+    }
+
+    /// <summary>
+    /// Returns the element next to the specified <paramref name="index"/> in the given 
+    /// <paramref name="enumerable"/>, in the direction specified by <paramref name="direction"/>, 
+    /// wrapping around if necessary.
+    /// </summary>
+    /// <typeparam name="T">The type of the elements in the enumerable.</typeparam>
+    /// <param name="enumerable">The source enumerable.</param>
+    /// <param name="index">The index of the reference element. Must be non-negative.</param>
+    /// <param name="direction">
+    /// The direction in which to look for the next element (forwards or backwards).
+    /// </param>
+    /// <returns>
+    /// The element next to the specified index in the chosen direction, with wrap-around behavior.
+    /// Returns <c>default</c> if the enumerable is empty.
+    /// </returns>
+    /// <remarks>
+    /// This method does not check whether the specified <paramref name="index"/> is within the 
+    /// bounds of the enumerable's size. Passing a positive out-of-range index may yield unexpected results.
+    /// </remarks>
+    /// <exception cref="ArgumentNullException">Thrown if <paramref name="enumerable"/> is <c>null</c>.</exception>
+    /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="index"/> is negative.</exception>
+    /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="direction"/> is not a valid <see cref="NextToDirection"/> value.</exception>
+    public static T? WrapInDirectionOfIndex<T>(this IEnumerable<T> enumerable, int index, NextToDirection direction) =>
+        direction switch
+        {
+            NextToDirection.Forwards => WrapNextAfterIndex(enumerable, index),
+            NextToDirection.Backwards => WrapPreviousBeforeIndex(enumerable, index),
+            _ => throw new ArgumentOutOfRangeException(nameof(direction)),
+        };
+
+    private static T? NextWithKnownCount<T>(IEnumerable<T> collection, int index, int count)
+    {
+        if (count == 0)
+            return default;
+        
+        var newIndex = index + 1;
+
+        if (newIndex < 0 || newIndex >= count)
+            newIndex = newIndex < 0 ? count - 1 : 0;
+        
+        return collection.ElementAtOrDefault(newIndex);
+    }
+}
+
+enum NextToDirection
+{
+    Forwards = 1,
+    Backwards = -1
 }

+ 15 - 10
src/PixiEditor/Helpers/Nodes/NodeAbbreviation.cs

@@ -7,7 +7,7 @@ public static class NodeAbbreviation
 {
     private static readonly SearchValues<char> SearchFor = SearchValues.Create(['.']);
 
-    
+
     public static bool IsAbbreviation(string value, out string? lastValue)
     {
         var span = value.AsSpan();
@@ -23,33 +23,38 @@ public static class NodeAbbreviation
         lastValue = span[(i + 1)..].ToString();
         return true;
     }
-    
+
     public static List<NodeTypeInfo>? FromString(string value, ICollection<NodeTypeInfo> allNodes)
     {
+        if (string.IsNullOrEmpty(value))
+        {
+            return null;
+        }
+
         var span = value.AsSpan();
 
+        string lookFor = value;
         if (!span.ContainsAny(SearchFor))
         {
-            return null;
+            return [allNodes.FirstOrDefault(SearchComparer)];
         }
-        
+
         var list = new List<NodeTypeInfo>();
 
         var enumerator = new PartEnumerator(span, SearchFor);
 
         foreach (var name in enumerator)
         {
-            var lookFor = name.Name.ToString();
+            lookFor = name.Name.ToString();
             var node = allNodes.First(SearchComparer);
 
             list.Add(node);
-
-            continue;
-
-            bool SearchComparer(NodeTypeInfo x) =>
-                x.FinalPickerName.Value.Replace(" ", "").Contains(lookFor.Replace(" ", ""), StringComparison.OrdinalIgnoreCase);
         }
 
+        bool SearchComparer(NodeTypeInfo x) =>
+            x.FinalPickerName.Value.Replace(" ", "")
+                .Contains(lookFor.Replace(" ", ""), StringComparison.OrdinalIgnoreCase);
+
         return list;
     }
 

+ 41 - 0
src/PixiEditor/Helpers/RenderApiPreferenceManager.cs

@@ -0,0 +1,41 @@
+namespace PixiEditor.Helpers;
+
+public static class RenderApiPreferenceManager
+{
+    public static string? FirstReadApiPreference { get; } = TryReadRenderApiPreference() ?? null;
+    public static string? TryReadRenderApiPreference()
+    {
+        try
+        {
+            using var stream =
+                new FileStream(
+                    Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+                        "PixiEditor",
+                        "render_api.config"), FileMode.Open, FileAccess.Read, FileShare.Read);
+            using var reader = new StreamReader(stream);
+            string? renderApi = reader.ReadLine();
+            if (string.IsNullOrEmpty(renderApi))
+            {
+                return null;
+            }
+
+            return renderApi;
+        }
+        catch (Exception)
+        {
+            return null;
+        }
+    }
+
+    public static void UpdateRenderApiPreference(string renderApi)
+    {
+        using var stream =
+            new FileStream(
+                Path.Combine(
+                    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+                    "PixiEditor",
+                    "render_api.config"), FileMode.Create, FileAccess.Write, FileShare.None);
+        using var writer = new StreamWriter(stream);
+        writer.WriteLine(renderApi);
+    }
+}

+ 7 - 12
src/PixiEditor/Initialization/ClassicDesktopEntry.cs

@@ -10,14 +10,12 @@ using PixiEditor.Extensions;
 using PixiEditor.Extensions.Runtime;
 using PixiEditor.Helpers;
 using PixiEditor.Helpers.Behaviours;
-using PixiEditor.IdentityProvider;
 using PixiEditor.Models.Controllers;
 using PixiEditor.Models.ExceptionHandling;
 using PixiEditor.Models.IO;
 using PixiEditor.OperatingSystem;
 using PixiEditor.Platform;
 using PixiEditor.UI.Common.Controls;
-using PixiEditor.ViewModels.SubViewModels;
 using PixiEditor.Views;
 using PixiEditor.Views.Auth;
 using PixiEditor.Views.Dialogs;
@@ -149,7 +147,7 @@ internal class ClassicDesktopEntry
         }
 
         ExtensionLoader extensionLoader = new ExtensionLoader(
-            [Paths.InstallDirExtensionPackagesPath, Paths.LocalExtensionPackagesPath], Paths.UserExtensionsPath);
+            [Paths.InstallDirExtensionPackagesPath, Paths.LocalExtensionPackagesPath], Paths.UnpackedExtensionsPath);
         if (!safeMode)
         {
             extensionLoader.LoadExtensions();
@@ -174,8 +172,8 @@ internal class ClassicDesktopEntry
         return new PixiEditor.Platform.MSStore.MicrosoftStorePlatform(Paths.LocalExtensionPackagesPath, GetApiUrl(),
             GetApiKey());
 #else
-        return new PixiEditor.Platform.Standalone.StandalonePlatform(Paths.LocalExtensionPackagesPath, GetApiUrl(),
-            GetApiKey());
+        return new PixiEditor.Platform.Standalone.StandalonePlatform([Paths.LocalExtensionPackagesPath, Paths.InstallDirExtensionPackagesPath], GetApiUrl(),
+            GetApiKey()); // The first in the extensionsPath array should be local, because it's the default where extensions are installed. Otherwise, OS access rights may cause issues.
 #endif
     }
 
@@ -194,7 +192,7 @@ internal class ClassicDesktopEntry
 #elif MACOS
         return new PixiEditor.MacOs.MacOperatingSystem();
 #else
-        throw new PlatformNotSupportedException("This platform is not supported");
+        throw new PlatformNotSupportedException("This OS is not supported");
 #endif
     }
 
@@ -260,10 +258,7 @@ internal class ClassicDesktopEntry
             if (restartQueued)
             {
                 var process = Process.GetCurrentProcess().MainModule.FileName;
-                desktop.Exit += (_, _) =>
-                {
-                    Process.Start(process);
-                };
+                Process.Start(process);
             }
         });
     }
@@ -280,7 +275,7 @@ internal class ClassicDesktopEntry
     {
         string? baseUrl = RuntimeConstants.PixiEditorApiUrl;
 #if DEBUG
-        if (baseUrl != null && baseUrl.Contains('{') && baseUrl.Contains('}'))
+        if (baseUrl == null)
         {
             string? envUrl = Environment.GetEnvironmentVariable("PIXIEDITOR_API_URL");
             if (envUrl != null)
@@ -297,7 +292,7 @@ internal class ClassicDesktopEntry
     {
         string? apiKey = RuntimeConstants.PixiEditorApiKey;
 #if DEBUG
-        if (apiKey != null && apiKey.Contains('{') && apiKey.Contains('}'))
+        if (apiKey == null)
         {
             string? envApiKey = Environment.GetEnvironmentVariable("PIXIEDITOR_API_KEY");
             if (envApiKey != null)

+ 4 - 1
src/PixiEditor/Models/AnalyticsAPI/AnalyticsPeriodicReporter.cs

@@ -33,7 +33,10 @@ public class AnalyticsPeriodicReporter
         _client = client;
         _performanceReporter = new PeriodicPerformanceReporter(this);
 
-        PixiEditorSettings.Analytics.AnalyticsEnabled.ValueChanged += EnableAnalyticsOnValueChanged;
+        if (PixiEditorSettings.Analytics.AnalyticsEnabled != null)
+        {
+            PixiEditorSettings.Analytics.AnalyticsEnabled.ValueChanged += EnableAnalyticsOnValueChanged;
+        }
     }
 
     public void Start(Guid? sessionId)

+ 12 - 2
src/PixiEditor/Models/Commands/XAML/NativeMenu.cs

@@ -65,9 +65,19 @@ internal class NativeMenu : global::Avalonia.Controls.Menu
         {
             if (!ShortcutController.ShortcutExecutionBlocked)
             {
-                if (iCommand.CanExecute(parameter))
+                if (command?.Shortcut != null && command.Shortcut.Gesture != null && command.Shortcut.Gesture.Key != Key.None || command.Shortcut.Gesture.KeyModifiers != KeyModifiers.None)
                 {
-                    iCommand.Execute(parameter);
+                     ViewModelMain.Current.ShortcutController.KeyPressed(
+                         false,
+                         command.Shortcut.Key,
+                         command.Shortcut.Modifiers);
+                }
+                else
+                {
+                    if (iCommand.CanExecute(parameter))
+                    {
+                        iCommand.Execute(parameter);
+                    }
                 }
             }
             else

+ 244 - 91
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -60,7 +60,7 @@ internal static class ClipboardController
     ///     2. Position of the copied area
     ///     3. Layers guid, this is used to duplicate the layer when pasting
     /// </summary>
-    public static async Task CopyToClipboard(DocumentViewModel document)
+    public static async Task CopyToClipboard(DocumentViewModel document, RectD? lastTransform)
     {
         await Clipboard.ClearAsync();
 
@@ -84,16 +84,19 @@ internal static class ClipboardController
             surfaceToCopy = surface.AsT2.Item1;
             copyArea = (RectD)surface.AsT2.Item2;
         }
-        else if (document.TransformViewModel.TransformActive)
+        else if (document.TransformViewModel.TransformActive || lastTransform != null)
         {
+            RectD transform = document.TransformViewModel.TransformActive
+                ? document.TransformViewModel.Corners.AABBBounds
+                : lastTransform.Value;
             var surface =
                 document.TryExtractAreaFromSelected(
-                    (RectI)document.TransformViewModel.Corners.AABBBounds.RoundOutwards());
+                    (RectI)transform.RoundOutwards());
             if (surface.IsT0 || surface.IsT1)
                 return;
 
             surfaceToCopy = surface.AsT2.Item1;
-            copyArea = document.TransformViewModel.Corners.AABBBounds;
+            copyArea = transform;
         }
         else if (document.SelectedStructureMember != null)
         {
@@ -147,7 +150,7 @@ internal static class ClipboardController
             copyArea = document.TransformViewModel.Corners.AABBBounds;
         }
 
-        if(copyArea.IsZeroOrNegativeArea || copyArea.HasNaNOrInfinity)
+        if (copyArea.IsZeroOrNegativeArea || copyArea.HasNaNOrInfinity)
         {
             NoticeDialog.Show("SELECTED_AREA_EMPTY", "NOTHING_TO_COPY");
             return;
@@ -183,8 +186,13 @@ internal static class ClipboardController
             await pngData.AsStream().CopyToAsync(pngStream);
 
             var pngArray = pngStream.ToArray();
-            data.Set(ClipboardDataFormats.Png, pngArray);
-            data.Set(ClipboardDataFormats.ImageSlashPng, pngArray);
+            foreach (string format in ClipboardDataFormats.PngFormats)
+            {
+                if (!data.Contains(format))
+                {
+                    data.Set(format, pngArray);
+                }
+            }
 
             pngStream.Position = 0;
             try
@@ -219,17 +227,19 @@ internal static class ClipboardController
     /// <summary>
     ///     Pastes image from clipboard into new layer.
     /// </summary>
-    public static bool TryPaste(DocumentViewModel document, DocumentManagerViewModel manager, IEnumerable<IDataObject>
-        data, bool pasteAsNew = false)
+    public static async Task<bool> TryPaste(DocumentViewModel document, DocumentManagerViewModel manager,
+        IImportObject[]
+            data, bool pasteAsNew = false)
     {
-        Guid sourceDocument = GetSourceDocument(data, document.Id);
-        Guid[] layerIds = GetLayerIds(data);
+        Guid sourceDocument = await GetSourceDocument(data, document.Id);
+        Guid[] layerIds = await GetLayerIds(data);
 
         bool hasPos = data.Any(x => x.Contains(ClipboardDataFormats.PositionFormat));
 
         IDocument? targetDoc = manager.Documents.FirstOrDefault(x => x.Id == sourceDocument);
 
-        if (targetDoc != null && pasteAsNew && layerIds is { Length: > 0 } && (!hasPos || AllMatchesPos(layerIds, data, targetDoc)))
+        if (targetDoc != null && pasteAsNew && layerIds is { Length: > 0 } &&
+            (!hasPos || await AllMatchesPos(layerIds, data, targetDoc)))
         {
             foreach (var layerId in layerIds)
             {
@@ -248,11 +258,11 @@ internal static class ClipboardController
             return true;
         }
 
-        List<DataImage> images = GetImage(data);
+        List<DataImage> images = await GetImage(data);
         if (images.Count == 0)
             return false;
 
-        if (images.Count == 1)
+        if (images.Count == 1 || (images.Count > 1 && !pasteAsNew))
         {
             var dataImage = images[0];
             var position = dataImage.Position;
@@ -289,16 +299,14 @@ internal static class ClipboardController
         return true;
     }
 
-    private static bool AllMatchesPos(Guid[] layerIds, IEnumerable<IDataObject> data, IDocument doc)
+    private static async Task<bool> AllMatchesPos(Guid[] layerIds, IImportObject[] dataFormats, IDocument doc)
     {
-        var dataObjects = data as IDataObject[] ?? data.ToArray();
-
-        var dataObjectWithPos = dataObjects.FirstOrDefault(x => x.Contains(ClipboardDataFormats.PositionFormat));
+        var dataObjectWithPos = dataFormats.FirstOrDefault(x => x.Contains(ClipboardDataFormats.PositionFormat));
         VecD pos = VecD.Zero;
 
         if (dataObjectWithPos != null)
         {
-            pos = dataObjectWithPos.GetVecD(ClipboardDataFormats.PositionFormat);
+            pos = await GetVecD(ClipboardDataFormats.PositionFormat, dataFormats);
         }
 
         RectD? tightBounds = null;
@@ -323,13 +331,13 @@ internal static class ClipboardController
         return tightBounds.HasValue && tightBounds.Value.Pos.AlmostEquals(pos);
     }
 
-    private static Guid[] GetLayerIds(IEnumerable<IDataObject> data)
+    private static async Task<Guid[]> GetLayerIds(IImportObject[] formats)
     {
-        foreach (var dataObject in data)
+        foreach (var dataObject in formats)
         {
             if (dataObject.Contains(ClipboardDataFormats.LayerIdList))
             {
-                byte[] layerIds = (byte[])dataObject.Get(ClipboardDataFormats.LayerIdList);
+                byte[] layerIds = await Clipboard.GetDataAsync(ClipboardDataFormats.LayerIdList) as byte[];
                 string layerIdsString = System.Text.Encoding.UTF8.GetString(layerIds);
                 return layerIdsString.Split(';').Select(Guid.Parse).ToArray();
             }
@@ -338,13 +346,14 @@ internal static class ClipboardController
         return [];
     }
 
-    private static Guid GetSourceDocument(IEnumerable<IDataObject> data, Guid fallback)
+    private static async Task<Guid> GetSourceDocument(IImportObject[] formats, Guid fallback)
     {
-        foreach (var dataObject in data)
+        foreach (var dataObject in formats)
         {
             if (dataObject.Contains(ClipboardDataFormats.DocumentFormat))
             {
-                byte[] guidBytes = (byte[])dataObject.Get(ClipboardDataFormats.DocumentFormat);
+                var data = await Clipboard.GetDataAsync(ClipboardDataFormats.DocumentFormat);
+                byte[] guidBytes = (byte[])data;
                 string guidString = System.Text.Encoding.UTF8.GetString(guidBytes);
                 return Guid.Parse(guidString);
             }
@@ -359,70 +368,68 @@ internal static class ClipboardController
     public static async Task<bool> TryPasteFromClipboard(DocumentViewModel document, DocumentManagerViewModel manager,
         bool pasteAsNew = false)
     {
-        var data = await TryGetDataObject();
+        var data = await TryGetImportObjects();
         if (data == null)
             return false;
 
-        return TryPaste(document, manager, data, pasteAsNew);
+        return await TryPaste(document, manager, data, pasteAsNew);
     }
 
-    private static async Task<List<DataObject?>> TryGetDataObject()
+    private static async Task<ClipboardPromiseObject[]> TryGetImportObjects()
     {
         string[] formats = await Clipboard.GetFormatsAsync();
         if (formats.Length == 0)
             return null;
 
-        List<DataObject?> dataObjects = new();
+        List<ClipboardPromiseObject?> dataObjects = new();
 
         for (int i = 0; i < formats.Length; i++)
         {
             string format = formats[i];
-            var obj = await Clipboard.GetDataAsync(format);
-
-            if (obj == null)
-                continue;
-
-            DataObject data = new DataObject();
-            data.Set(format, obj);
-
-            dataObjects.Add(data);
+            dataObjects.Add(new ClipboardPromiseObject(format, Clipboard));
         }
 
-        return dataObjects;
+        return dataObjects.ToArray();
     }
 
     public static async Task<List<DataImage>> GetImagesFromClipboard()
     {
-        var dataObj = await TryGetDataObject();
-        return GetImage(dataObj);
+        var dataObj = await TryGetImportObjects();
+        return await GetImage(dataObj);
     }
 
     /// <summary>
     /// Gets images from clipboard, supported PNG and Bitmap.
     /// </summary>
-    public static List<DataImage> GetImage(IEnumerable<IDataObject?> data)
+    public static async Task<List<DataImage>> GetImage(IImportObject[] importableObjects)
     {
         List<DataImage> surfaces = new();
 
-        if (data == null)
+        if (importableObjects == null)
             return surfaces;
 
         VecD pos = VecD.Zero;
 
-        foreach (var dataObject in data)
+        string? importingType = null;
+        bool pngImported = false;
+
+        foreach (var dataObject in importableObjects)
         {
-            if (TryExtractSingleImage(dataObject, out var singleImage))
+            var img = await TryExtractSingleImage(dataObject);
+            if (importingType is null or "bytes" && img != null && !pngImported)
             {
-                surfaces.Add(new DataImage(singleImage,
+                surfaces.Add(new DataImage(img,
                     dataObject.Contains(ClipboardDataFormats.PositionFormat)
-                        ? (VecI)dataObject.GetVecD(ClipboardDataFormats.PositionFormat)
+                        ? (VecI)await GetVecD(ClipboardDataFormats.PositionFormat, importableObjects)
                         : (VecI)pos));
+                importingType = "bytes";
+                pngImported = true;
                 continue;
             }
 
             if (dataObject.Contains(ClipboardDataFormats.PositionFormat))
             {
-                pos = dataObject.GetVecD(ClipboardDataFormats.PositionFormat);
+                pos = await GetVecD(ClipboardDataFormats.PositionFormat, importableObjects);
                 for (var i = 0; i < surfaces.Count; i++)
                 {
                     var surface = surfaces[i];
@@ -430,13 +437,14 @@ internal static class ClipboardController
                 }
             }
 
-            var paths = dataObject.GetFileDropList().Select(x => x.Path.LocalPath).ToList();
-            if (paths != null && dataObject.TryGetRawTextPath(out string? textPath))
+            var paths = (await GetFileDropList(dataObject))?.Select(x => x.Path.LocalPath).ToList();
+            string[]? rawPaths = await TryGetRawTextPaths(dataObject);
+            if (paths != null && rawPaths != null)
             {
-                paths.Add(textPath);
+                paths.AddRange(rawPaths);
             }
 
-            if (paths == null || paths.Count == 0)
+            if (paths == null || paths.Count == 0 || (importingType != null && importingType != "files"))
             {
                 continue;
             }
@@ -462,7 +470,8 @@ internal static class ClipboardController
 
                     string filename = Path.GetFullPath(path);
                     surfaces.Add(new DataImage(filename, imported,
-                        (VecI)dataObject.GetVecD(ClipboardDataFormats.PositionFormat)));
+                        (VecI)await GetVecD(ClipboardDataFormats.PositionFormat, importableObjects)));
+                    importingType = "files";
                 }
                 catch
                 {
@@ -474,6 +483,104 @@ internal static class ClipboardController
         return surfaces;
     }
 
+    private static async Task<string[]?> TryGetRawTextPaths(IImportObject importObj)
+    {
+        if (!importObj.Contains(DataFormats.Text) && !importObj.Contains(ClipboardDataFormats.UriList))
+        {
+            return null;
+        }
+
+        string text = null;
+        try
+        {
+            text = await importObj.GetDataAsync(DataFormats.Text) as string;
+        }
+        catch(InvalidCastException ex) // bug on x11
+        {
+        }
+
+        string[] paths = [text];
+        if (text == null)
+        {
+            if (await importObj.GetDataAsync(ClipboardDataFormats.UriList) is byte[] bytes)
+            {
+                paths = Encoding.UTF8.GetString(bytes).Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
+            }
+        }
+
+        if (paths.Length == 0)
+        {
+            return null;
+        }
+
+        List<string> validPaths = new();
+
+        foreach (string path in paths)
+        {
+            if (string.IsNullOrWhiteSpace(path))
+                continue;
+
+            if (Directory.Exists(path) || File.Exists(path))
+            {
+                validPaths.Add(path);
+            }
+            else
+            {
+                try
+                {
+                    Uri uri = new Uri(path);
+                    if (uri.IsAbsoluteUri && (Directory.Exists(uri.LocalPath) || File.Exists(uri.LocalPath)))
+                    {
+                        validPaths.Add(uri.LocalPath);
+                    }
+                }
+                catch (UriFormatException)
+                {
+                    // Ignore invalid URIs
+                }
+            }
+        }
+
+        return validPaths.Count > 0 ? validPaths.ToArray() : null;
+    }
+
+    private static async Task<IEnumerable<IStorageItem>> GetFileDropList(IImportObject obj)
+    {
+        if (!obj.Contains(DataFormats.Files))
+            return [];
+
+        var data = await obj.GetDataAsync(DataFormats.Files);
+        if (data == null)
+            return [];
+
+        if (data is IEnumerable<IStorageItem> storageItems)
+            return storageItems;
+
+        if (data is Task<object> task)
+        {
+            data = await task;
+            if (data is IEnumerable<IStorageItem> storageItemsFromTask)
+                return storageItemsFromTask;
+        }
+
+        return [];
+    }
+
+
+    private static async Task<VecD> GetVecD(string format, IImportObject[] availableFormats)
+    {
+        var firstFormat = availableFormats.FirstOrDefault(x => x.Contains(format));
+        if (firstFormat == null)
+            return new VecD(-1, -1);
+
+        byte[] bytes = (byte[])await firstFormat.GetDataAsync(format);
+
+        if (bytes is { Length: < 16 })
+            return new VecD(-1, -1);
+
+        return VecD.FromBytes(bytes);
+    }
+
     public static bool IsImage(IDataObject? dataObject)
     {
         if (dataObject == null)
@@ -495,7 +602,7 @@ internal static class ClipboardController
             return false;
         }
 
-        return HasData(dataObject, ClipboardDataFormats.Png, ClipboardDataFormats.ImageSlashPng);
+        return HasData(dataObject, ClipboardDataFormats.PngFormats);
     }
 
     public static async Task<bool> IsImageInClipboard()
@@ -509,7 +616,20 @@ internal static class ClipboardController
         if (!isImage)
         {
             string path = await TryFindImageInFiles(formats);
-            return Path.Exists(path);
+            try
+            {
+                Uri uri = new Uri(path);
+                return Path.Exists(uri.LocalPath);
+            }
+            catch (UriFormatException)
+            {
+                return false;
+            }
+            catch (Exception ex)
+            {
+                CrashHelper.SendExceptionInfo(ex);
+                return false;
+            }
         }
 
         return isImage;
@@ -519,15 +639,7 @@ internal static class ClipboardController
     {
         foreach (string format in formats)
         {
-            if (format == DataFormats.Text)
-            {
-                string text = await ClipboardController.GetTextFromClipboard();
-                if (Importer.IsSupportedFile(text))
-                {
-                    return text;
-                }
-            }
-            else if (format == DataFormats.Files)
+            if (format == DataFormats.Files || format == ClipboardDataFormats.UriList)
             {
                 var files = await ClipboardController.Clipboard.GetDataAsync(format);
                 if (files is IEnumerable<IStorageItem> storageFiles)
@@ -547,6 +659,28 @@ internal static class ClipboardController
                         }
                     }
                 }
+
+                if (files is byte[] bytes)
+                {
+                    string utf8String = Encoding.UTF8.GetString(bytes);
+                    string[] paths = utf8String.Split(['\n', '\r'], StringSplitOptions.RemoveEmptyEntries);
+                    foreach (string path in paths)
+                    {
+                        if (Importer.IsSupportedFile(path))
+                        {
+                            return path;
+                        }
+                    }
+                }
+
+                if (format == DataFormats.Text)
+                {
+                    string text = await ClipboardController.GetTextFromClipboard();
+                    if (Importer.IsSupportedFile(text))
+                    {
+                        return text;
+                    }
+                }
             }
         }
 
@@ -557,7 +691,7 @@ internal static class ClipboardController
     {
         foreach (var format in formats)
         {
-            if (format == ClipboardDataFormats.Png)
+            if (ClipboardDataFormats.PngFormats.Contains(format, StringComparer.OrdinalIgnoreCase))
             {
                 return true;
             }
@@ -571,41 +705,58 @@ internal static class ClipboardController
         return false;
     }
 
-    private static Bitmap FromPNG(IDataObject data)
+    private static async Task<Surface?> FromPNG(IImportObject importObj)
     {
-        object obj = data.Get("PNG");
-        if (obj is byte[] bytes)
+        object? pngData = null;
+        foreach (string format in ClipboardDataFormats.PngFormats)
+        {
+            if (importObj.Contains(format))
+            {
+                object? data = await importObj.GetDataAsync(format);
+                if (data == null)
+                    continue;
+
+                pngData = data;
+                break;
+            }
+        }
+
+        if (pngData is byte[] bytes)
         {
-            using MemoryStream stream = new MemoryStream(bytes);
-            return new Bitmap(stream);
+            return Surface.Load(bytes);
         }
 
-        if (obj is MemoryStream memoryStream)
+        if (pngData is MemoryStream memoryStream)
         {
-            return new Bitmap(memoryStream);
+            bytes = memoryStream.ToArray();
+            return Surface.Load(bytes);
         }
 
-        throw new InvalidDataException("PNG data is not in a supported format.");
+        return null;
     }
 
     private static bool HasData(IDataObject dataObject, params string[] formats) => formats.Any(dataObject.Contains);
 
-    private static bool TryExtractSingleImage(IDataObject data, [NotNullWhen(true)] out Surface? result)
+    private static async Task<Surface?> TryExtractSingleImage(IImportObject importedObj)
     {
         try
         {
-            Bitmap source;
-            if (data.Contains(ClipboardDataFormats.Png) || data.Contains(ClipboardDataFormats.ImageSlashPng))
+            Surface source;
+            bool dataContainsPng = ClipboardDataFormats.PngFormats.Any(importedObj.Contains);
+            if (dataContainsPng)
             {
-                source = FromPNG(data);
+                source = await FromPNG(importedObj);
+                if (source == null)
+                {
+                    return null;
+                }
             }
             else
             {
-                result = null;
-                return false;
+                return null;
             }
 
-            if (source.Format.Value.IsSkiaSupported())
+            /*if (source.Format.Value.IsSkiaSupported())
             {
                 result = SurfaceHelpers.FromBitmap(source);
             }
@@ -616,14 +767,13 @@ internal static class ClipboardController
                     source.Dpi, source.PixelSize.Width * 4);
 
                 result = SurfaceHelpers.FromBitmap(newFormat);
-            }
+            }*/
 
-            return true;
+            return source;
         }
         catch { }
 
-        result = null;
-        return false;
+        return null;
     }
 
     public static async Task CopyNodes(Guid[] nodeIds, Guid docId)
@@ -643,17 +793,20 @@ internal static class ClipboardController
 
     public static async Task<Guid[]> GetIds(string format)
     {
-        var data = await TryGetDataObject();
-        return GetIds(data, format);
+        var data = await TryGetImportObjects();
+        return await GetIds(data, format);
     }
 
-    private static Guid[] GetIds(IEnumerable<IDataObject?> data, string format)
+
+    private static async Task<Guid[]> GetIds(IEnumerable<IImportObject?> data, string format)
     {
         foreach (var dataObject in data)
         {
             if (dataObject.Contains(format))
             {
-                byte[] nodeIds = (byte[])dataObject.Get(format);
+                byte[] nodeIds = await dataObject.GetDataAsync(format) as byte[];
+                if (nodeIds == null || nodeIds.Length == 0)
+                    return [];
                 string nodeIdsString = System.Text.Encoding.UTF8.GetString(nodeIds);
                 return nodeIdsString.Split(';').Select(Guid.Parse).ToArray();
             }
@@ -695,7 +848,7 @@ internal static class ClipboardController
         data.Set(ClipboardDataFormats.DocumentFormat, Encoding.UTF8.GetBytes(docId.ToString()));
 
         byte[] idsBytes = Encoding.UTF8.GetBytes(string.Join(";", ids.Select(x => x.ToString())));
-
+        
         data.Set(format, idsBytes);
 
         await Clipboard.SetDataObjectAsync(data);
@@ -703,7 +856,7 @@ internal static class ClipboardController
 
     public static async Task<Guid> GetDocumentId()
     {
-        var data = await TryGetDataObject();
+        var data = await TryGetImportObjects();
         if (data == null)
             return Guid.Empty;
 
@@ -711,7 +864,7 @@ internal static class ClipboardController
         {
             if (dataObject.Contains(ClipboardDataFormats.DocumentFormat))
             {
-                byte[] guidBytes = (byte[])dataObject.Get(ClipboardDataFormats.DocumentFormat);
+                byte[] guidBytes = (byte[])await dataObject.GetDataAsync(ClipboardDataFormats.DocumentFormat);
                 string guidString = System.Text.Encoding.UTF8.GetString(guidBytes);
                 return Guid.Parse(guidString);
             }

+ 62 - 0
src/PixiEditor/Models/Controllers/IImportObject.cs

@@ -0,0 +1,62 @@
+using Avalonia.Input;
+using Avalonia.Input.Platform;
+
+namespace PixiEditor.Models.Controllers;
+
+public interface IImportObject
+{
+    public bool Contains(string format);
+    public Task<object?> GetDataAsync(string format);
+}
+
+public class ImportedObject : IImportObject
+{
+    private readonly IDataObject dataObject;
+
+    public ImportedObject(IDataObject dataObject)
+    {
+        this.dataObject = dataObject;
+    }
+
+    public bool Contains(string format)
+    {
+        return dataObject.Contains(format);
+    }
+
+    public async Task<object?> GetDataAsync(string format)
+    {
+        return Task.FromResult(dataObject.Get(format));
+    }
+}
+
+public class ClipboardPromiseObject : IImportObject
+{
+    public string Format { get; }
+    public IClipboard Clipboard { get; }
+
+    public ClipboardPromiseObject(string format, IClipboard clipboard)
+    {
+        Format = format;
+        Clipboard = clipboard;
+    }
+
+    public bool Contains(string format)
+    {
+        return Format == format;
+    }
+
+    public async Task<object?> GetDataAsync(string format)
+    {
+        if (Format != format)
+        {
+            return null;
+        }
+
+        return await Clipboard.GetDataAsync(format);
+    }
+
+    public override string ToString()
+    {
+        return $"ClipboardPromiseObject: {Format}";
+    }
+}

+ 9 - 0
src/PixiEditor/Models/DocumentModels/DocumentTransformMode.cs

@@ -3,12 +3,21 @@
 namespace PixiEditor.Models.DocumentModels;
 internal enum DocumentTransformMode
 {
+    // Comments show localization in DocumentTransformViewModel.cs, comments are needed for localization key check pipeline
+    
+    // TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE
     [Description("SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE")]
     Scale_NoRotate_NoShear_NoPerspective,
+    
+    // TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE
     [Description("SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE")]
     Scale_Rotate_NoShear_NoPerspective,
+    
+    // TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE
     [Description("SCALE_ROTATE_SHEAR_NOPERSPECTIVE")]
     Scale_Rotate_Shear_NoPerspective,
+    
+    // TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_PERSPECTIVE
     [Description("SCALE_ROTATE_SHEAR_PERSPECTIVE")]
     Scale_Rotate_Shear_Perspective
 }

+ 4 - 2
src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs

@@ -835,8 +835,10 @@ internal class DocumentUpdater
         property.Errors = info.Errors;
 
         ProcessStructureMemberProperty(info, property);
-
-        property.InternalSetValue(info.Value);
+        var toSet = property.IsFunc
+            ? (info.Value as ShaderExpressionVariable)?.GetConstant() ?? info.Value
+            : info.Value;
+        property.InternalSetValue(toSet);
 
         if (info.Property == CustomOutputNode.OutputNamePropertyName)
         {

+ 26 - 3
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -76,8 +76,10 @@ internal class DocumentOperationsModule : IDocumentOperations
     /// <summary>
     /// Deletes selected pixels
     /// </summary>
+    /// <param name="frame"></param>
     /// <param name="clearSelection">Should the selection be cleared</param>
-    public void DeleteSelectedPixels(int frame, bool clearSelection = false)
+    /// <param name="lastTransformRect"></param>
+    public void DeleteSelectedPixels(int frame, bool clearSelection = false, RectD? lastTransformRect = null)
     {
         var member = Document.SelectedStructureMember;
         if (Internals.ChangeController.IsBlockingChangeActive || member is null)
@@ -85,11 +87,22 @@ internal class DocumentOperationsModule : IDocumentOperations
 
         Internals.ChangeController.TryStopActiveExecutor();
 
+        VectorPath? selection = Internals.Tracker.Document.Selection?.SelectionPath ?? null;
+
+        if (selection == null || selection.IsEmpty)
+        {
+            selection = new VectorPath();
+            if (lastTransformRect is not null)
+            {
+                selection.AddRect(lastTransformRect.Value);
+            }
+        }
+
         bool drawOnMask = member is not ILayerHandler layer || layer.ShouldDrawOnMask;
         if (drawOnMask && !member.HasMaskBindable)
             return;
         Internals.ActionAccumulator.AddActions(new ClearSelectedArea_Action(member.Id,
-            Internals.Tracker.Document.Selection.SelectionPath, drawOnMask, frame));
+            selection, drawOnMask, frame));
         if (clearSelection)
             Internals.ActionAccumulator.AddActions(new ClearSelection_Action());
         Internals.ActionAccumulator.AddFinishedActions();
@@ -149,6 +162,8 @@ internal class DocumentOperationsModule : IDocumentOperations
 
         Internals.ChangeController.TryStopActiveExecutor();
 
+        var changeBlock = Document.Operations.StartChangeBlock();
+
         RectI maxSize = new RectI(VecI.Zero, Document.SizeBindable);
         foreach (var imageWithName in images)
         {
@@ -167,7 +182,7 @@ internal class DocumentOperationsModule : IDocumentOperations
                 layerGuid, true, false, frame, false);
         }
 
-        Internals.ActionAccumulator.AddFinishedActions();
+        changeBlock.Dispose();
     }
 
     /// <summary>
@@ -1017,4 +1032,12 @@ internal class DocumentOperationsModule : IDocumentOperations
         Internals.ActionAccumulator.AddFinishedActions(
             new ExtractSelectedText_Action(memberId, startIndex, endIndex, extractEachCharacter));
     }
+
+    public void TryStopActiveExecutor()
+    {
+        if (Internals.ChangeController.IsBlockingChangeActive)
+            return;
+
+        Internals.ChangeController.TryStopActiveExecutor();
+    }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/PasteImageExecutor.cs

@@ -97,7 +97,7 @@ internal class PasteImageExecutor : UpdateableChangeExecutor, ITransformableExec
     public override void ForceStop()
     {
         document!.TransformHandler.HideTransform();
-        internals!.ActionAccumulator.AddActions(new EndPasteImage_Action());
+        internals!.ActionAccumulator.AddFinishedActions(new EndPasteImage_Action());
     }
 
     public bool IsFeatureEnabled<T>()

+ 4 - 1
src/PixiEditor/Models/IO/Paths.cs

@@ -21,7 +21,7 @@ public static class Paths
         Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
         "PixiEditor", "Configs");
 
-    public static string UserExtensionsPath { get; } = Path.Combine(
+    public static string UnpackedExtensionsPath { get; } = Path.Combine(
         Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
         "PixiEditor", "Extensions", "Unpacked");
 
@@ -44,6 +44,9 @@ public static class Paths
         Path.GetTempPath(),
         "PixiEditor", "Autosave");
 
+    public static string InstallDirectoryPath { get; } =
+        Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) ?? string.Empty;
+
     public static string ParseSpecialPathOrDefault(string path)
     {
         path = path.Replace("%appdata%", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));

+ 15 - 3
src/PixiEditor/Models/Preferences/PreferencesSettings.cs

@@ -2,6 +2,9 @@
 using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
+using PixiEditor.Helpers;
+using PixiEditor.Models.Dialogs;
+using PixiEditor.UI.Common.Localization;
 
 namespace PixiEditor.Models.Preferences;
 
@@ -12,7 +15,7 @@ internal class PreferencesSettings : IPreferences
 
     public string PathToRoamingUserPreferences { get; private set; } = GetPathToSettings(Environment.SpecialFolder.ApplicationData, "user_preferences.json");
 
-    public string PathToLocalPreferences { get; private set; } = GetPathToSettings(Environment.SpecialFolder.LocalApplicationData, "2_0_beta_editor_data.json");
+    public string PathToLocalPreferences { get; private set; } = GetPathToSettings(Environment.SpecialFolder.LocalApplicationData, "editor_data.json");
 
     public Dictionary<string, object> Preferences { get; set; } = new Dictionary<string, object>();
 
@@ -89,8 +92,17 @@ internal class PreferencesSettings : IPreferences
             Init();
         }
 
-        File.WriteAllText(PathToRoamingUserPreferences, JsonConvert.SerializeObject(Preferences));
-        File.WriteAllText(PathToLocalPreferences, JsonConvert.SerializeObject(LocalPreferences));
+        try
+        {
+            File.WriteAllText(PathToRoamingUserPreferences, JsonConvert.SerializeObject(Preferences));
+            File.WriteAllText(PathToLocalPreferences, JsonConvert.SerializeObject(LocalPreferences));
+        }
+        catch (Exception ex)
+        {
+            NoticeDialog.Show(
+                new LocalizedString("ERROR_SAVING_PREFERENCES_DESC", ex.Message),
+                "ERROR_SAVING_PREFERENCES");
+        }
     }
 
     public Dictionary<string, List<Action<string, object>>> Callbacks { get; set; } = new Dictionary<string, List<Action<string, object>>>();

+ 18 - 2
src/PixiEditor/Models/Rendering/PreviewPainter.cs

@@ -23,6 +23,10 @@ public class PreviewPainter : IDisposable
     public VecI DocumentSize { get; set; }
     public DocumentRenderer Renderer { get; set; }
 
+    public bool CanRender => canRender;
+
+    public event Action<bool>? CanRenderChanged;
+
     private Dictionary<int, Texture> renderTextures = new();
     private Dictionary<int, PainterInstance> painterInstances = new();
 
@@ -32,6 +36,8 @@ public class PreviewPainter : IDisposable
     private Dictionary<int, VecI> pendingResizes = new();
     private HashSet<int> pendingRemovals = new();
 
+    private bool canRender;
+
     private int lastRequestId = 0;
 
     public PreviewPainter(DocumentRenderer renderer, IPreviewRenderable previewRenderable, KeyFrameTime frameTime,
@@ -126,11 +132,21 @@ public class PreviewPainter : IDisposable
         RepaintDirty();
     }
 
-
-
     private void RepaintDirty()
     {
         var dirtyArray = dirtyTextures.ToArray();
+        bool couldRender = canRender;
+        canRender = PreviewRenderable?.GetPreviewBounds(FrameTime.Frame, ElementToRenderName) != null;
+        if (couldRender != canRender)
+        {
+            CanRenderChanged?.Invoke(canRender);
+        }
+
+        if (!CanRender)
+        {
+            return;
+        }
+
         foreach (var texture in dirtyArray)
         {
             if (!renderTextures.TryGetValue(texture, out var renderTexture))

+ 2 - 2
src/PixiEditor/PixiEditor.csproj

@@ -91,7 +91,7 @@
     <PackageReference Include="AsyncImageLoader.Avalonia" Version="3.3.0"/>
     <PackageReference Include="Avalonia" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Headless" Version="$(AvaloniaVersion)"/>
-    <PackageReference Include="Avalonia.Labs.Lottie" Version="11.3.0" />
+    <PackageReference Include="Avalonia.Labs.Lottie" Version="11.3.1" />
     <PackageReference Include="Avalonia.Themes.Fluent" Version="$(AvaloniaVersion)"/>
     <PackageReference Include="Avalonia.Skia" Version="$(AvaloniaVersion)"/>
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
@@ -105,7 +105,7 @@
     <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
     <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
-    <PackageReference Include="Svg.Controls.Skia.Avalonia" Version="11.3.0.1" />
+    <PackageReference Include="Svg.Controls.Skia.Avalonia" Version="11.3.0.3" />
   </ItemGroup>
 
   <ItemGroup>

+ 2 - 2
src/PixiEditor/Properties/AssemblyInfo.cs

@@ -43,5 +43,5 @@ using System.Runtime.InteropServices;
 // You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("2.0.1.0")]
-[assembly: AssemblyFileVersion("2.0.1.0")]
+[assembly: AssemblyVersion("2.0.1.9")]
+[assembly: AssemblyFileVersion("2.0.1.9")]

+ 16 - 6
src/PixiEditor/RuntimeConstants.cs

@@ -1,14 +1,24 @@
+using System.Diagnostics;
+using System.Reflection;
+using PixiEditor.Models.IO;
+
 namespace PixiEditor;
 
 public static class RuntimeConstants
 {
-    private static Dictionary<string, string> appSettings = File.Exists("appsettings.json")
-        ? System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(ReadAppSettings())
-        : new Dictionary<string, string>();
+    private static Dictionary<string, string> appSettings =
+        System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(ReadAppSettings());
 
     private static string ReadAppSettings()
     {
-        using StreamReader reader = new StreamReader("appsettings.json");
+        string installDirPath = Paths.InstallDirectoryPath;
+        string appsettingsPath = Path.Combine(installDirPath, "appsettings.json");
+        if (!File.Exists(appsettingsPath))
+        {
+            return "{}";
+        }
+        
+        using StreamReader reader = new StreamReader(appsettingsPath);
         return reader.ReadToEnd();
     }
 
@@ -16,9 +26,9 @@ public static class RuntimeConstants
     public static string? AnalyticsUrl =>
         appSettings.TryGetValue("AnalyticsUrl", out string? url) ? url : null;
 
-    public static string? PixiEditorApiUrl =
+    public static string? PixiEditorApiUrl =>
         appSettings.TryGetValue("PixiEditorApiUrl", out string? apiUrl) ? apiUrl : null;
 
-    public static string? PixiEditorApiKey =
+    public static string? PixiEditorApiKey =>
         appSettings.TryGetValue("PixiEditorApiKey", out string? apiKey) ? apiKey : null;
 }

+ 17 - 35
src/PixiEditor/Styles/Templates/NodeGraphView.axaml

@@ -69,41 +69,23 @@
                             </ControlTheme>
                         </ItemsControl.ItemContainerTheme>
                     </ItemsControl>
-                    <ItemsControl Name="PART_Connections"
-                                  ItemsSource="{Binding NodeGraph.Connections, RelativeSource={RelativeSource TemplatedParent}}">
-                        <ItemsControl.ItemsPanel>
-                            <ItemsPanelTemplate>
-                                <Canvas RenderTransformOrigin="0, 0">
-                                    <Canvas.RenderTransform>
-                                        <TransformGroup>
-                                            <ScaleTransform
-                                                ScaleX="{Binding Scale, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}"
-                                                ScaleY="{Binding Scale, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}" />
-                                            <TranslateTransform
-                                                X="{Binding CanvasX, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}"
-                                                Y="{Binding CanvasY, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}" />
-                                        </TransformGroup>
-                                    </Canvas.RenderTransform>
-                                </Canvas>
-                            </ItemsPanelTemplate>
-                        </ItemsControl.ItemsPanel>
-                        <ItemsControl.ItemTemplate>
-                            <DataTemplate>
-                                <nodes:ConnectionView
-                                    InputNodePosition="{Binding InputNode.PositionBindable}"
-                                    OutputNodePosition="{Binding OutputNode.PositionBindable}"
-                                    InputProperty="{Binding InputProperty}"
-                                    OutputProperty="{Binding OutputProperty}">
-                                    <nodes:ConnectionView.IsVisible>
-                                        <MultiBinding Converter="{x:Static BoolConverters.And}">
-                                            <Binding Path="InputProperty.IsVisible" />
-                                            <Binding Path="OutputProperty.IsVisible" />
-                                        </MultiBinding>
-                                    </nodes:ConnectionView.IsVisible>
-                                </nodes:ConnectionView>
-                            </DataTemplate>
-                        </ItemsControl.ItemTemplate>
-                    </ItemsControl>
+                    <nodes:ConnectionRenderer
+                        ClipToBounds="False"
+                        Name="PART_ConnectionRenderer"
+                        RenderTransformOrigin="0, 0"
+                        SocketsInfo="{Binding SocketsInfo, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}"
+                        Connections="{Binding NodeGraph.Connections, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}">
+                        <nodes:ConnectionRenderer.RenderTransform>
+                            <TransformGroup>
+                                <ScaleTransform
+                                    ScaleX="{Binding Scale, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}"
+                                    ScaleY="{Binding Scale, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}" />
+                                <TranslateTransform
+                                    X="{Binding CanvasX, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}"
+                                    Y="{Binding CanvasY, RelativeSource={RelativeSource FindAncestor, AncestorType=nodes:NodeGraphView}}" />
+                            </TransformGroup>
+                        </nodes:ConnectionRenderer.RenderTransform>
+                    </nodes:ConnectionRenderer>
                     <ItemsControl
                         ZIndex="-1"
                         Name="PART_Frames"

+ 15 - 1
src/PixiEditor/Styles/Templates/NodePicker.axaml

@@ -4,7 +4,8 @@
                     xmlns:visuals="clr-namespace:PixiEditor.Views.Visuals"
                     xmlns:ui="clr-namespace:PixiEditor.UI.Common.Localization;assembly=PixiEditor.UI.Common"
                     xmlns:input="clr-namespace:PixiEditor.Views.Input"
-                    xmlns:nodes1="clr-namespace:PixiEditor.ViewModels.Nodes">
+                    xmlns:nodes1="clr-namespace:PixiEditor.ViewModels.Nodes"
+                    xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters">
     <ControlTheme TargetType="nodes:NodePicker" x:Key="{x:Type nodes:NodePicker}">
         <Setter Property="Template">
             <ControlTemplate>
@@ -55,6 +56,19 @@
                                                         CommandParameter="{Binding}"
                                                         HorizontalContentAlignment="Left"
                                                         IsVisible="{Binding !Hidden}">
+                                                        <Classes.KeyboardSelected>
+                                                            <MultiBinding Converter="{converters:AreEqualConverter}">
+                                                                <Binding />
+                                                                <Binding Path="SelectedNode" RelativeSource="{RelativeSource FindAncestor, AncestorType=nodes:NodePicker}" Mode="OneWay"/>
+                                                            </MultiBinding>
+                                                        </Classes.KeyboardSelected>
+                                                        
+                                                        <Button.Styles>
+                                                            <Style Selector="Button.KeyboardSelected">
+                                                                <Setter Property="Background" Value="{DynamicResource ThemeControlHighlightBrush}" />
+                                                            </Style>
+                                                        </Button.Styles>
+                                                        
                                                         <TextBlock Margin="10 0 0 0">
                                                             <Run Classes="pixi-icon"
                                                                  BaselineAlignment="Center"

+ 1 - 1
src/PixiEditor/Styles/Templates/NodeView.axaml

@@ -56,7 +56,7 @@
                                 </ItemsControl>
                             </StackPanel>
                         </Border>
-                        <Border IsVisible="{Binding !!ResultPreview, RelativeSource={RelativeSource TemplatedParent}}"
+                        <Border IsVisible="{Binding CanRenderPreview, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=False}"
                                 CornerRadius="0, 0, 4.5, 4.5" Grid.Row="2" ClipToBounds="True">
                             <Panel RenderOptions.BitmapInterpolationMode="None" Width="200" Height="200">
                                 <Panel.Background>

+ 10 - 0
src/PixiEditor/ViewModels/DockableViewModel.cs

@@ -1,6 +1,7 @@
 using Avalonia.Media;
 using PixiDocks.Core.Docking;
 using PixiDocks.Core.Docking.Events;
+using PixiEditor.UI.Common.Localization;
 using PixiEditor.ViewModels.Dock;
 
 namespace PixiEditor.ViewModels;
@@ -15,5 +16,14 @@ internal abstract class DockableViewModel : ViewModelBase, IDockableContent
 
     public DockableViewModel()
     {
+        if (ILocalizationProvider.Current != null)
+        {
+            ILocalizationProvider.Current.OnLanguageChanged += OnLanguageChanged;
+        }
+    }
+
+    private void OnLanguageChanged(Language language)
+    {
+        OnPropertyChanged(nameof(Title));
     }
 }

+ 3 - 2
src/PixiEditor/ViewModels/Document/AnimationDataViewModel.cs

@@ -466,12 +466,13 @@ internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
 
     public int GetFirstVisibleFrame()
     {
-        return keyFrames.Count > 0 ? keyFrames.Where(x => x.IsVisible).Min(x => x.StartFrameBindable) : 1;
+        return keyFrames.Count > 0 && keyFrames.Any(x => x.IsVisible)
+            ? keyFrames.Where(x => x.IsVisible).Min(x => x.StartFrameBindable) : 1;
     }
 
     public int GetLastVisibleFrame()
     {
-        return keyFrames.Count > 0
+        return keyFrames.Count > 0 && keyFrames.Any(x => x.IsVisible)
             ? keyFrames.Where(x => x.IsVisible).Max(x => x.StartFrameBindable + x.DurationBindable)
             : DefaultEndFrameBindable;
     }

+ 1 - 1
src/PixiEditor/ViewModels/Document/CelGroupViewModel.cs

@@ -23,7 +23,7 @@ internal class CelGroupViewModel : CelViewModel, ICelGroupHandler
 
     public string LayerName => Document.StructureHelper.Find(LayerGuid).NodeNameBindable;
 
-    public bool IsGroupSelected => Document.SelectedStructureMember.Id == LayerGuid;
+    public bool IsGroupSelected => Document?.SelectedStructureMember?.Id == LayerGuid;
 
     public bool IsCollapsed
     {

+ 5 - 1
src/PixiEditor/ViewModels/Document/CelViewModel.cs

@@ -91,7 +91,11 @@ internal abstract class CelViewModel : ObservableObject, ICelHandler
     public bool IsSelected
     {
         get => isSelected;
-        set => SetProperty(ref isSelected, value);
+        set
+        {
+            if (StartFrameBindable == 0) return;
+            SetProperty(ref isSelected, value);
+        }
     }
 
     protected CelViewModel(int startFrame, int duration, Guid layerGuid, Guid id,

+ 4 - 1
src/PixiEditor/ViewModels/Document/DocumentViewModel.Serialization.cs

@@ -314,7 +314,10 @@ internal partial class DocumentViewModel
         var path = new SvgPath();
         if (data.Path != null)
         {
-            string pathData = data.Path.ToSvgPathData();
+            // This is super strange, we got reports that on linux with different locales, numbers are separated by commas
+            // and not dots. Comma separated svg data is invalid. This is raw Skia call, there is no other place where it could be changed.
+            // That's why we replace commas with dots here. I really hope skia never uses commas as a number separator
+            string pathData = data.Path.ToSvgPathData().Replace(",", ".");
             path.PathData.Unit = new SvgStringUnit(pathData);
             SvgFillRule fillRule = data.Path.FillType switch
             {

+ 16 - 9
src/PixiEditor/ViewModels/Document/DocumentViewModel.cs

@@ -445,7 +445,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                 {
                     object value =
                         SerializationUtil.Deserialize(propertyValue.Value, config, allFactories, serializerData);
-                    acc.AddActions(new UpdatePropertyValue_Action(guid, propertyValue.Key, value));
+                    acc.AddActions(new UpdatePropertyValue_Action(guid, propertyValue.Key, value), new EndUpdatePropertyValue_Action());
                 }
             }
 
@@ -852,7 +852,7 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
         if (finalBounds.IsZeroOrNegativeArea)
             return new None();
 
-        Surface output = new(finalBounds.Size);
+        Surface output = Surface.ForDisplay(finalBounds.Size);
 
         VectorPath clipPath = new VectorPath(SelectionPathBindable) { FillType = PathFillType.EvenOdd };
         //clipPath.Transform(Matrix3X3.CreateTranslation(-bounds.X, -bounds.Y));
@@ -1302,13 +1302,20 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
 
     public void Dispose()
     {
-        NodeGraph.Dispose();
-        Renderer.Dispose();
-        SceneRenderer.Dispose();
-        AnimationDataViewModel.Dispose();
-        Internals.ChangeController.TryStopActiveExecutor();
-        Internals.Tracker.Dispose();
-        Internals.Tracker.Document.Dispose();
+        try
+        {
+            NodeGraph.Dispose();
+            Renderer.Dispose();
+            SceneRenderer.Dispose();
+            AnimationDataViewModel.Dispose();
+            Internals.ChangeController.TryStopActiveExecutor();
+            Internals.Tracker.Dispose();
+            Internals.Tracker.Document.Dispose();
+        }
+        catch (Exception ex)
+        {
+            CrashHelper.SendExceptionInfo(ex);
+        }
     }
 
     public VecI GetRenderOutputSize(string renderOutputName)

+ 11 - 1
src/PixiEditor/ViewModels/Document/NodeGraphViewModel.cs

@@ -228,7 +228,17 @@ internal class NodeGraphViewModel : ViewModelBase, INodeGraphHandler, IDisposabl
 
     public void UpdatePropertyValue(INodeHandler node, string property, object? value)
     {
-        Internals.ActionAccumulator.AddFinishedActions(new UpdatePropertyValue_Action(node.Id, property, value));
+        Internals.ActionAccumulator.AddFinishedActions(new UpdatePropertyValue_Action(node.Id, property, value), new EndUpdatePropertyValue_Action());
+    }
+
+    public void BeginUpdatePropertyValue(INodeHandler node, string property, object value)
+    {
+        Internals.ActionAccumulator.AddActions(new UpdatePropertyValue_Action(node.Id, property, value));
+    }
+
+    public void EndUpdatePropertyValue()
+    {
+        Internals.ActionAccumulator.AddFinishedActions(new EndUpdatePropertyValue_Action());
     }
 
     public void RequestUpdateComputedPropertyValue(INodePropertyHandler property)

+ 9 - 0
src/PixiEditor/ViewModels/Document/Nodes/Shapes/LineNodeViewModel.cs

@@ -0,0 +1,9 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Shapes;
+
+[NodeViewModel("LINE_NODE", "SHAPE", PixiPerfectIcons.Line)]
+internal class LineNodeViewModel : NodeViewModel<LineNode>
+{
+}

+ 9 - 0
src/PixiEditor/ViewModels/Document/Nodes/Shapes/RectangleNodeViewModel.cs

@@ -0,0 +1,9 @@
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes;
+using PixiEditor.ViewModels.Nodes;
+
+namespace PixiEditor.ViewModels.Document.Nodes.Shapes;
+
+[NodeViewModel("RECTANGLE_NODE", "SHAPE", PixiPerfectIcons.Square)]
+internal class RectangleNodeViewModel : NodeViewModel<RectangleNode>
+{
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä