Forráskód Böngészése

Merge branch 'master' into mask-improvements

CPK 3 hete
szülő
commit
3c8c605651
52 módosított fájl, 2315 hozzáadás és 214 törlés
  1. 23 0
      .github/workflows/localization-key-reference.yml
  2. 1 0
      .github/workflows/localization/check-key-references.pip.txt
  3. 70 0
      .github/workflows/localization/check-key-references.py
  4. 1 1
      src/ColorPicker
  5. 1 1
      src/Directory.Build.props
  6. 1 1
      src/Drawie
  7. 1 1
      src/PixiDocks
  8. 6 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/FuncInputProperty.cs
  9. 163 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  10. 1 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/PointsVectorData.cs
  11. 32 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/LineNode.cs
  12. 37 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/RectangleNode.cs
  13. 3 0
      src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs
  14. 2 2
      src/PixiEditor.ChangeableDocument/Changes/Root/ClipCanvas_Change.cs
  15. 1 0
      src/PixiEditor.IdentityProvider.PixiAuth/PixiAuthIdentityProvider.cs
  16. 4 0
      src/PixiEditor.UI.Common/Accents/Base.axaml
  17. 12 28
      src/PixiEditor/Data/Localization/Languages/en.json
  18. 1130 0
      src/PixiEditor/Data/Localization/Languages/tr.json
  19. 8 1
      src/PixiEditor/Data/Localization/LocalizationData.json
  20. 2 2
      src/PixiEditor/Helpers/Constants/ClipboardDataFormats.cs
  21. 27 17
      src/PixiEditor/Helpers/Converters/EnumToLocalizedStringConverter.cs
  22. 0 19
      src/PixiEditor/Helpers/EnumDescriptionConverter.cs
  23. 13 5
      src/PixiEditor/Helpers/Extensions/DataObjectExtensions.cs
  24. 135 1
      src/PixiEditor/Helpers/Extensions/EnumerableExtensions.cs
  25. 18 0
      src/PixiEditor/Helpers/LocalizeEnumAttribute.cs
  26. 12 2
      src/PixiEditor/Models/Commands/XAML/NativeMenu.cs
  27. 227 81
      src/PixiEditor/Models/Controllers/ClipboardController.cs
  28. 62 0
      src/PixiEditor/Models/Controllers/IImportObject.cs
  29. 9 0
      src/PixiEditor/Models/DocumentModels/DocumentTransformMode.cs
  30. 100 0
      src/PixiEditor/Models/EnumTranslations.cs
  31. 5 1
      src/PixiEditor/Models/Handlers/Toolbars/PaintBrushShape.cs
  32. 5 1
      src/PixiEditor/Models/Tools/BrightnessMode.cs
  33. 2 2
      src/PixiEditor/Properties/AssemblyInfo.cs
  34. 15 1
      src/PixiEditor/Styles/Templates/NodePicker.axaml
  35. 5 1
      src/PixiEditor/ViewModels/Document/CelViewModel.cs
  36. 29 1
      src/PixiEditor/ViewModels/Document/Nodes/NoiseNodeViewModel.cs
  37. 9 0
      src/PixiEditor/ViewModels/Document/Nodes/Shapes/LineNodeViewModel.cs
  38. 9 0
      src/PixiEditor/ViewModels/Document/Nodes/Shapes/RectangleNodeViewModel.cs
  39. 1 1
      src/PixiEditor/ViewModels/SubViewModels/AnimationsViewModel.cs
  40. 2 2
      src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs
  41. 1 1
      src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs
  42. 3 1
      src/PixiEditor/ViewModels/SubViewModels/LayersViewModel.cs
  43. 8 4
      src/PixiEditor/Views/Dialogs/ExportFilePopup.axaml.cs
  44. 2 1
      src/PixiEditor/Views/Layers/LayersManager.axaml.cs
  45. 5 9
      src/PixiEditor/Views/Main/CommandSearch/CommandSearchControl.axaml.cs
  46. 6 2
      src/PixiEditor/Views/MainView.axaml.cs
  47. 82 8
      src/PixiEditor/Views/Nodes/NodePicker.cs
  48. 2 2
      src/PixiEditor/Views/Nodes/Properties/NodePropertyView.cs
  49. 8 1
      src/PixiEditor/Views/Overlays/BrushShapeOverlay/BrushShape.cs
  50. 10 2
      src/PixiEditor/Views/Overlays/Handles/Handle.cs
  51. 3 4
      src/PixiEditor/Views/Tools/ToolSettings/Settings/EnumSettingView.axaml
  52. 1 1
      tests/Directory.Build.props

+ 23 - 0
.github/workflows/localization-key-reference.yml

@@ -0,0 +1,23 @@
+name: Localization Reference Key Check
+
+on:
+  push:
+    branches: [ "master" ]
+  pull_request:
+    branches: [ "master" ]
+
+jobs:
+  check-key-references:
+    name: "Check Localization Keys are referenced"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
+        with:
+          python-version: '3.13'
+          cache: 'pip'
+          cache-dependency-path: .github/workflows/localization/check-key-references.pip.txt
+      - name: Install dependencies
+        run: pip install -r .github/workflows/localization/check-key-references.pip.txt
+      - name: Check Localization Key References
+        run: python .github/workflows/localization/check-key-references.py

+ 1 - 0
.github/workflows/localization/check-key-references.pip.txt

@@ -0,0 +1 @@
+pyahocorasick

+ 70 - 0
.github/workflows/localization/check-key-references.py

@@ -0,0 +1,70 @@
+import json
+import os
+import logging
+from collections import OrderedDict
+import time
+import ahocorasick
+
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger()
+
+# PATHS
+REFERENCE_LANGUAGE = "src/PixiEditor/Data/Localization/Languages/en.json"
+SEARCH_DIRECTORIES = ["src/PixiEditor/Views", "src/PixiEditor/ViewModels", "src/PixiEditor", "src/"]
+IGNORE_DIRECTORIES = ["src/PixiEditor/Data/Localization"]
+
+def load_json(file_path):
+    """Load language JSON"""
+    try:
+        with open(file_path, "r", encoding="utf-8-sig") as f:
+            return json.load(f, object_pairs_hook=OrderedDict)
+    except FileNotFoundError:
+        print(f"::error::File not found: {file_path}")
+        return OrderedDict()
+    except json.JSONDecodeError as e:
+        print(f"::error::Failed to parse JSON in {file_path}: {e}")
+        return OrderedDict()
+
+def build_automaton(keys: list[str]) -> ahocorasick.Automaton:
+    A = ahocorasick.Automaton()
+    for i, k in enumerate(keys):
+        A.add_word(k, (i, k))
+    A.make_automaton()
+    return A
+
+def find_missing_keys(keys):
+    automaton = build_automaton(keys)
+    present = set()
+
+    ignore_prefixes = tuple(os.path.abspath(p) for p in IGNORE_DIRECTORIES)
+    for base_dir in SEARCH_DIRECTORIES:
+        for root, dirs, files in os.walk(base_dir, topdown=True):
+            dirs[:] = [d for d in dirs if not os.path.abspath(os.path.join(root, d)).startswith(ignore_prefixes)]
+            for file in files:
+                with open(os.path.join(root, file), "r", encoding="utf‑8", errors="ignore") as f:
+                    for _, (_, k) in automaton.iter(f.read()):
+                        present.add(k)
+                        if len(present) == len(keys):
+                            return []
+    return sorted(set(keys) - present)
+
+def main():
+    keys = load_json(REFERENCE_LANGUAGE)
+
+    print("Searching trough keys...")
+    start = time.time()
+    missing_keys = find_missing_keys(keys)
+    end = time.time()
+    print(f"Done, searching took {end - start}s")
+
+    if len(missing_keys) > 0:
+        print("Unreferenced keys have been found")
+        for key in missing_keys:
+            print(f"::error file={REFERENCE_LANGUAGE},title=Unreferenced key::No reference to '{key}' found")
+        return 1
+    else:
+        print("All keys have been referenced")
+        return 0
+    
+if __name__ == "__main__":
+    exit(main())

+ 1 - 1
src/ColorPicker

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

+ 1 - 1
src/Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
         <CodeAnalysisRuleSet>../Custom.ruleset</CodeAnalysisRuleSet>
-		    <AvaloniaVersion>11.3.3</AvaloniaVersion>
+		    <AvaloniaVersion>11.3.0</AvaloniaVersion>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit bd34e239887d69235d18f0f95abdf80f94534b5d
+Subproject commit 1be85ac9f4bc6b584e6a3a5a3d0287201c6a5f03

+ 1 - 1
src/PixiDocks

@@ -1 +1 @@
-Subproject commit cb36e5ed4b61b99d52461e353d009ef52cb7d97b
+Subproject commit 6e745d0309ad7a00a53f62f2aa362be77903a5fd

+ 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;

+ 163 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs

@@ -18,6 +18,11 @@ public class NoiseNode : RenderNode
     private NoiseType previousNoiseType = Nodes.NoiseType.FractalPerlin;
     private int previousOctaves = -1;
     private VecD previousOffset = new VecD(0d, 0d);
+    private VoronoiFeature previousVoronoiFeature = Nodes.VoronoiFeature.F1;
+    private double previousRandomness = double.NaN;
+    private double previousAngleOffset = double.NaN;
+    
+    private Shader voronoiShader;
 
     private Paint paint = new();
 
@@ -25,7 +30,7 @@ public class NoiseNode : RenderNode
         ColorMatrix.MapAlphaToRedGreenBlue + ColorMatrix.OpaqueAlphaOffset);
 
     public InputProperty<NoiseType> NoiseType { get; }
-
+    
     public InputProperty<VecD> Offset { get; }
     
     public InputProperty<double> Scale { get; }
@@ -33,6 +38,12 @@ public class NoiseNode : RenderNode
     public InputProperty<int> Octaves { get; }
 
     public InputProperty<double> Seed { get; }
+    
+    public InputProperty<VoronoiFeature> VoronoiFeature { get; }
+    
+    public InputProperty<double> Randomness { get; }
+    
+    public InputProperty<double> AngleOffset { get; }
 
     public NoiseNode()
     {
@@ -45,6 +56,13 @@ public class NoiseNode : RenderNode
             .WithRules(validator => validator.Min(1));
 
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
+        
+        VoronoiFeature = CreateInput(nameof(VoronoiFeature), "VORONOI_FEATURE", Nodes.VoronoiFeature.F1);
+        
+        Randomness = CreateInput(nameof(Randomness), "RANDOMNESS", 1d)
+            .WithRules(v => v.Min(0d).Max(1d));
+        
+        AngleOffset = CreateInput(nameof(AngleOffset), "ANGLE_OFFSET", 0d);
     }
 
     protected override void OnPaint(RenderContext context, DrawingSurface target)
@@ -54,6 +72,9 @@ public class NoiseNode : RenderNode
             || previousOctaves != Octaves.Value
             || previousNoiseType != NoiseType.Value
             || previousOffset != Offset.Value
+            || previousVoronoiFeature != VoronoiFeature.Value
+            || Math.Abs(previousRandomness - Randomness.Value) > 0.000001
+            || Math.Abs(previousAngleOffset - AngleOffset.Value) > 0.000001
             || double.IsNaN(previousScale))
         {
             if (Scale.Value < 0.000001)
@@ -67,6 +88,10 @@ public class NoiseNode : RenderNode
                 return;
             }
 
+            if (paint.Shader != voronoiShader)
+            {
+                paint?.Shader?.Dispose();
+            }
             paint.Shader = shader;
 
             // Define a grayscale color filter to apply to the image
@@ -76,6 +101,9 @@ public class NoiseNode : RenderNode
             previousSeed = Seed.Value;
             previousOctaves = Octaves.Value;
             previousNoiseType = NoiseType.Value;
+            previousVoronoiFeature = VoronoiFeature.Value;
+            previousRandomness = Randomness.Value;
+            previousAngleOffset = AngleOffset.Value;
         }
 
         RenderNoise(target);
@@ -101,7 +129,11 @@ public class NoiseNode : RenderNode
         {
             return false;
         }
-        
+
+        if (paint.Shader != voronoiShader)
+        {
+            paint?.Shader?.Dispose();
+        }
         paint.Shader = shader;
         paint.ColorFilter = grayscaleFilter;
         
@@ -122,17 +154,145 @@ public class NoiseNode : RenderNode
                 (float)(1d / Scale.Value),
                 (float)(1d / Scale.Value),
                 octaves, (float)Seed.Value),
+            Nodes.NoiseType.Voronoi => GetVoronoiShader((float)(1d / (Scale.Value)), octaves, (float)Seed.Value, (int)VoronoiFeature.Value, (float)Randomness.Value, (float)AngleOffset.Value),
             _ => null
         };
 
         return shader;
     }
 
+    private Shader GetVoronoiShader(float frequency, int octaves, float seed, int feature, float randomness, float angleOffset)
+    {
+        string voronoiShaderCode = """
+                                   uniform float iSeed;
+                                   uniform float iFrequency;
+                                   uniform int iOctaves;
+                                   uniform float iRandomness;
+                                   uniform int iFeature;
+                                   uniform float iAngleOffset;
+                                   
+                                   const int MAX_OCTAVES = 8;
+                                   const float LARGE_NUMBER = 1e9;
+                                   const float FEATURE_SEED_SCALE = 10.0;
+                                   const float PI = 3.14159265;
+                                   
+                                   float hashPoint(float2 p, float seed) {
+                                       p = fract(p * float2(0.3183099, 0.3678794) + seed);
+                                       p += dot(p, p.yx + 19.19);
+                                       return fract(p.x * p.y);
+                                   }
+                                   
+                                   float2 getFeaturePoint(float2 cell, float seed, float randomness, float angleOffset) {
+                                       float2 randomCellOffset = float2(
+                                           hashPoint(cell, seed),
+                                           hashPoint(cell, seed + 17.0)
+                                       );
+                                       
+                                       float2 featurePoint = mix(float2(0.5, 0.5), randomCellOffset, randomness);
+                                       
+                                       float angle = hashPoint(cell, seed + 53.0) * PI * 2.0;
+                                       angle += angleOffset;
+                                       
+                                       float2 dir = float2(cos(angle), sin(angle));
+                                       float offsetAmount = 0.15;
+                                       featurePoint += dir * offsetAmount * randomness;
+                                       
+                                       featurePoint = clamp(featurePoint, 0.0, 1.0);
+                                       
+                                       return featurePoint;
+                                   }
+                                   
+                                   float2 getVoronoiDistances(float2 pos, float seed) {
+                                       float2 cell = floor(pos);
+                                       float minDist = LARGE_NUMBER;
+                                       float secondMinDist = LARGE_NUMBER;
+                                   
+                                       for (int y = -1; y <= 1; y++) {
+                                           for (int x = -1; x <= 1; x++) {
+                                               float2 neighborCell = cell + float2(float(x), float(y));
+                                               float2 featurePoint = getFeaturePoint(neighborCell, seed, iRandomness, iAngleOffset);
+                                               float2 delta = pos - (neighborCell + featurePoint);
+                                               float dist = length(delta);
+                                               
+                                               if (dist < minDist) {
+                                                   secondMinDist = minDist;
+                                                   minDist = dist;
+                                               } else if (dist < secondMinDist) {
+                                                   secondMinDist = dist;
+                                               }
+                                           }
+                                       }
+                                       return float2(minDist, secondMinDist);
+                                   }
+                                   
+                                   half4 main(float2 uv) {
+                                       float noiseSum = 0.0;
+                                       float amplitude = 1.0;
+                                       float amplitudeSum = 0.0;
+                                   
+                                       for (int octave = 0; octave < MAX_OCTAVES; octave++) {
+                                           if (octave >= iOctaves) break;
+                                   
+                                           float freq = iFrequency * exp2(float(octave));
+                                           float2 samplePos = uv * freq;
+                                           
+                                           float dist = 0.0;
+                                           float2 distances = getVoronoiDistances(samplePos, iSeed + float(octave) * FEATURE_SEED_SCALE);
+                                           float f1 = distances.x;
+                                           float f2 = distances.y;
+                                           
+                                           if (iFeature == 0) {
+                                               dist = f1;
+                                           }
+                                           else if (iFeature == 1) {
+                                               dist = f2;
+                                           }
+                                           else if (iFeature == 2) {
+                                               dist = f2 - f1;
+                                           }
+                                   
+                                           noiseSum += dist * amplitude;
+                                           amplitudeSum += amplitude;
+                                           amplitude *= 0.5;
+                                       }
+                                   
+                                       return half4(noiseSum / amplitudeSum);
+                                   }
+                                   """;
+        
+        Uniforms uniforms = new Uniforms();
+        uniforms.Add("iSeed", new Uniform("iSeed", seed));
+        uniforms.Add("iFrequency", new Uniform("iFrequency", frequency));
+        uniforms.Add("iOctaves", new Uniform("iOctaves", octaves));
+        uniforms.Add("iRandomness", new Uniform("iRandomness", randomness));
+        uniforms.Add("iFeature", new Uniform("iFeature", feature));
+        uniforms.Add("iAngleOffset", new Uniform("iAngleOffset", angleOffset));
+
+        if (voronoiShader == null)
+        {
+            voronoiShader = Shader.Create(voronoiShaderCode, uniforms, out _);
+        }
+        else
+        {
+            voronoiShader = voronoiShader.WithUpdatedUniforms(uniforms);
+        }
+        
+        return voronoiShader;
+    }
+
     public override Node CreateCopy() => new NoiseNode();
 }
 
 public enum NoiseType
 {
     TurbulencePerlin,
-    FractalPerlin
+    FractalPerlin,
+    Voronoi
+}
+
+public enum VoronoiFeature
+{
+    F1 = 0, // Distance to the closest feature point
+    F2 = 1, // Distance to the second-closest feature point
+    F2MinusF1 = 2
 }

+ 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();
+}

+ 3 - 0
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs

@@ -115,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;

+ 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 - 0
src/PixiEditor.IdentityProvider.PixiAuth/PixiAuthIdentityProvider.cs

@@ -129,6 +129,7 @@ public class PixiAuthIdentityProvider : IIdentityProvider
             Error(e.Message, e.TimeLeft);
             LoginTimeout?.Invoke(e.TimeLeft);
         }
+        // Can produce SESSION_NOT_FOUND or USER_NOT_FOUND as a message, this comment ensure it's catched by the localization key checker
         catch (PixiAuthException authException)
         {
             Error(authException.Message);

+ 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}" />
 

+ 12 - 28
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",
@@ -967,6 +946,13 @@
   "RAW_COLOR_SAMPLE_MODE": "Raw",
   "FRACTAL_PERLIN_NOISE_TYPE": "Perlin",
   "TURBULENCE_PERLIN_NOISE_TYPE": "Turbulence",
+  "VORONOI_NOISE_TYPE": "Voronoi",
+  "VORONOI_FEATURE": "Feature",
+  "F1_VORONOI_FEATURE": "F1",
+  "F2_VORONOI_FEATURE": "F2",
+  "F2_MINUS_F1_VORONOI_FEATURE": "F2-F1",
+  "RANDOMNESS": "Randomness",
+  "ANGLE_OFFSET": "Angle Offset",
   "INHERIT_COLOR_SPACE_TYPE": "Inherit",
   "SRGB_COLOR_SPACE_TYPE": "sRGB",
   "LINEAR_SRGB_COLOR_SPACE_TYPE": "Linear sRGB",
@@ -994,7 +980,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 +1042,6 @@
   "SECONDARY_BG_COLOR": "Secondary background color",
   "RESET": "Reset",
   "INSTALL": "Install",
-  "MANAGE_ACCOUNT": "Manage",
   "OWNED_PRODUCTS": "Owned Content",
   "INSTALLING": "Installing",
   "INSTALLED": "Installed",
@@ -1074,7 +1058,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",
@@ -1109,6 +1092,7 @@
   "REMAP_NODE": "Remap",
   "TEXT_TOOL_ACTION_DISPLAY": "Click on the canvas to add a new text (drag while clicking to set the size). Click on existing text to edit it.",
   "PASTE_CELS": "Paste cels",
+  "PASTE_CELS_DESCRIPTIVE": "Paste cels from clipboard into the current frame",
   "SCALE_X": "Scale X",
   "SCALE_Y": "Scale Y",
   "TRANSLATE_X": "Translate X",

+ 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"
+}

+ 8 - 1
src/PixiEditor/Data/Localization/LocalizationData.json

@@ -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";
 }

+ 27 - 17
src/PixiEditor/Helpers/Converters/EnumToLocalizedStringConverter.cs

@@ -1,4 +1,6 @@
-using System.Globalization;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
 using PixiEditor.Extensions.Helpers;
 using PixiEditor.UI.Common.Localization;
 
@@ -6,30 +8,38 @@ namespace PixiEditor.Helpers.Converters;
 
 internal class EnumToLocalizedStringConverter : SingleInstanceConverter<EnumToLocalizedStringConverter>
 {
+    private Dictionary<object, string> enumTranslations = new(
+        typeof(EnumToLocalizedStringConverter).Assembly
+            .GetCustomAttributes()
+            .OfType<ILocalizeEnumInfo>()
+            .Select(x => new KeyValuePair<object, string>(x.GetEnumValue(), x.LocalizationKey)));
+    
     public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
-        if (value is Enum enumValue)
+        if (value is not Enum enumValue)
         {
-            if (EnumHelpers.HasDescription(enumValue))
-            {
-                return EnumHelpers.GetDescription(enumValue);
-            }
+            return value;
+        }
+
+        if (enumTranslations.TryGetValue(enumValue, out var assemblyDefinedKey))
+        {
+            return assemblyDefinedKey;
+        }
 
-            return ToLocalizedStringFormat(enumValue);
+        if (EnumHelpers.HasDescription(enumValue))
+        {
+            return EnumHelpers.GetDescription(enumValue);
         }
 
-        return value;
+        ThrowUntranslatedEnumValue(enumValue);
+        return enumValue;
     }
 
-    private string ToLocalizedStringFormat(Enum enumValue)
+    [Conditional("DEBUG")]
+    private static void ThrowUntranslatedEnumValue(object value)
     {
-        // VALUE_ENUMTYPE
-        // for example BlendMode.Normal becomes NORMAL_BLEND_MODE
-
-        string enumType = enumValue.GetType().Name;
-
-        string value = enumValue.ToString();
-
-        return $"{value.ToSnakeCase()}_{enumType.ToSnakeCase()}".ToUpper();
+        throw new ArgumentException(
+            $"Enum value '{value.GetType()}.{value}' has no value defined. Either add a Description attribute to the enum values or a LocalizeEnum attribute in EnumTranslations.cs for third party enums",
+            nameof(value));
     }
 }

+ 0 - 19
src/PixiEditor/Helpers/EnumDescriptionConverter.cs

@@ -1,19 +0,0 @@
-using System.Globalization;
-using PixiEditor.Extensions.Helpers;
-using PixiEditor.Helpers.Converters;
-using PixiEditor.UI.Common.Localization;
-
-namespace PixiEditor.Helpers;
-
-internal class EnumDescriptionConverter : SingleInstanceConverter<EnumDescriptionConverter>
-{
-    public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-    {
-        if (value is Enum enumValue)
-        {
-            return EnumHelpers.GetDescription(enumValue);
-        }
-
-        return value;
-    }
-}

+ 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
 }

+ 18 - 0
src/PixiEditor/Helpers/LocalizeEnumAttribute.cs

@@ -0,0 +1,18 @@
+namespace PixiEditor.Helpers;
+
+public interface ILocalizeEnumInfo
+{
+    public object GetEnumValue();
+    
+    public string LocalizationKey { get; }
+}
+
+[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+public class LocalizeEnumAttribute<T>(T value, string key) : Attribute, ILocalizeEnumInfo where T : Enum
+{
+    public T Value { get; } = value;
+
+    object ILocalizeEnumInfo.GetEnumValue() => Value;
+    
+    public string LocalizationKey { get; } = key;
+}

+ 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

+ 227 - 81
src/PixiEditor/Models/Controllers/ClipboardController.cs

@@ -86,7 +86,8 @@ internal static class ClipboardController
         }
         else if (document.TransformViewModel.TransformActive || lastTransform != null)
         {
-            RectD transform = document.TransformViewModel.TransformActive ? document.TransformViewModel.Corners.AABBBounds
+            RectD transform = document.TransformViewModel.TransformActive
+                ? document.TransformViewModel.Corners.AABBBounds
                 : lastTransform.Value;
             var surface =
                 document.TryExtractAreaFromSelected(
@@ -149,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;
@@ -185,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
@@ -221,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)
             {
@@ -250,7 +258,7 @@ internal static class ClipboardController
             return true;
         }
 
-        List<DataImage> images = GetImage(data);
+        List<DataImage> images = await GetImage(data);
         if (images.Count == 0)
             return false;
 
@@ -291,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;
@@ -325,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();
             }
@@ -340,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);
             }
@@ -361,73 +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;
 
         string? importingType = null;
+        bool pngImported = false;
 
-        foreach (var dataObject in data)
+        foreach (var dataObject in importableObjects)
         {
-            if (importingType is null or "bytes" && 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];
@@ -435,10 +437,11 @@ 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 || (importingType != null && importingType != "files"))
@@ -467,7 +470,7 @@ 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
@@ -480,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)
@@ -501,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()
@@ -515,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;
@@ -525,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)
@@ -553,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;
+                    }
+                }
             }
         }
 
@@ -563,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;
             }
@@ -577,38 +705,55 @@ internal static class ClipboardController
         return false;
     }
 
-    private static Surface 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)
         {
             return Surface.Load(bytes);
         }
 
-        if (obj is MemoryStream memoryStream)
+        if (pngData is MemoryStream 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
         {
             Surface source;
-            if (data.Contains(ClipboardDataFormats.Png) || data.Contains(ClipboardDataFormats.ImageSlashPng))
+            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())
@@ -624,13 +769,11 @@ internal static class ClipboardController
                 result = SurfaceHelpers.FromBitmap(newFormat);
             }*/
 
-            result = source;
-            return true;
+            return source;
         }
         catch { }
 
-        result = null;
-        return false;
+        return null;
     }
 
     public static async Task CopyNodes(Guid[] nodeIds, Guid docId)
@@ -650,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();
             }
@@ -702,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);
@@ -710,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;
 
@@ -718,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
 }

+ 100 - 0
src/PixiEditor/Models/EnumTranslations.cs

@@ -0,0 +1,100 @@
+using Drawie.Backend.Core.Shaders.Generation;
+using Drawie.Backend.Core.Surfaces;
+using Drawie.Backend.Core.Surfaces.PaintImpl;
+using Drawie.Backend.Core.Vector;
+using PixiEditor.AnimationRenderer.Core;
+using PixiEditor.ChangeableDocument.Changeables.Graph.ColorSpaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Animable;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Effects;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.FilterNodes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Matrix;
+using PixiEditor.Helpers;
+
+[assembly: LocalizeEnum<StrokeCap>(StrokeCap.Butt, "BUTT_STROKE_CAP")]
+[assembly: LocalizeEnum<StrokeCap>(StrokeCap.Round, "ROUND_STROKE_CAP")]
+[assembly: LocalizeEnum<StrokeCap>(StrokeCap.Square, "SQUARE_STROKE_CAP")]
+
+[assembly: LocalizeEnum<StrokeJoin>(StrokeJoin.Bevel, "BEVEL_STROKE_JOIN")]
+[assembly: LocalizeEnum<StrokeJoin>(StrokeJoin.Round, "ROUND_STROKE_JOIN")]
+[assembly: LocalizeEnum<StrokeJoin>(StrokeJoin.Miter, "MITER_STROKE_JOIN")]
+
+[assembly: LocalizeEnum<GrayscaleNode.GrayscaleMode>(GrayscaleNode.GrayscaleMode.Weighted, "WEIGHTED_GRAYSCALE_MODE")]
+[assembly: LocalizeEnum<GrayscaleNode.GrayscaleMode>(GrayscaleNode.GrayscaleMode.Average, "AVERAGE_GRAYSCALE_MODE")]
+[assembly: LocalizeEnum<GrayscaleNode.GrayscaleMode>(GrayscaleNode.GrayscaleMode.Custom, "CUSTOM_GRAYSCALE_MODE")]
+
+[assembly: LocalizeEnum<ColorSampleMode>(ColorSampleMode.ColorManaged, "COLOR_MANAGED_COLOR_SAMPLE_MODE")]
+[assembly: LocalizeEnum<ColorSampleMode>(ColorSampleMode.Raw, "RAW_COLOR_SAMPLE_MODE")]
+
+[assembly: LocalizeEnum<TileMode>(TileMode.Clamp, "CLAMP_TILE_MODE")]
+[assembly: LocalizeEnum<TileMode>(TileMode.Decal, "DECAL_TILE_MODE")]
+[assembly: LocalizeEnum<TileMode>(TileMode.Mirror, "MIRROR_TILE_MODE")]
+[assembly: LocalizeEnum<TileMode>(TileMode.Repeat, "REPEAT_TILE_MODE")]
+
+[assembly: LocalizeEnum<CombineSeparateColorMode>(CombineSeparateColorMode.RGB, "R_G_B_COMBINE_SEPARATE_COLOR_MODE")] 
+[assembly: LocalizeEnum<CombineSeparateColorMode>(CombineSeparateColorMode.HSV, "H_S_V_COMBINE_SEPARATE_COLOR_MODE")]
+[assembly: LocalizeEnum<CombineSeparateColorMode>(CombineSeparateColorMode.HSL, "H_S_L_COMBINE_SEPARATE_COLOR_MODE")]
+
+[assembly: LocalizeEnum<VectorPathOp>(VectorPathOp.Difference, "DIFFERENCE_VECTOR_PATH_OP")]
+[assembly: LocalizeEnum<VectorPathOp>(VectorPathOp.Intersect, "INTERSECT_VECTOR_PATH_OP")]
+[assembly: LocalizeEnum<VectorPathOp>(VectorPathOp.Union, "UNION_VECTOR_PATH_OP")]
+[assembly: LocalizeEnum<VectorPathOp>(VectorPathOp.Xor, "XOR_VECTOR_PATH_OP")]
+[assembly: LocalizeEnum<VectorPathOp>(VectorPathOp.ReverseDifference, "REVERSE_DIFFERENCE_VECTOR_PATH_OP")]
+
+[assembly: LocalizeEnum<QualityPreset>(QualityPreset.VeryLow, "VERY_LOW_QUALITY_PRESET")]
+[assembly: LocalizeEnum<QualityPreset>(QualityPreset.Low, "LOW_QUALITY_PRESET")]
+[assembly: LocalizeEnum<QualityPreset>(QualityPreset.Medium, "MEDIUM_QUALITY_PRESET")]
+[assembly: LocalizeEnum<QualityPreset>(QualityPreset.High, "HIGH_QUALITY_PRESET")]
+[assembly: LocalizeEnum<QualityPreset>(QualityPreset.VeryHigh, "VERY_HIGH_QUALITY_PRESET")]
+
+[assembly: LocalizeEnum<ColorSpaceType>(ColorSpaceType.Inherit, "INHERIT_COLOR_SPACE_TYPE")]
+[assembly: LocalizeEnum<ColorSpaceType>(ColorSpaceType.Srgb, "SRGB_COLOR_SPACE_TYPE")]
+[assembly: LocalizeEnum<ColorSpaceType>(ColorSpaceType.LinearSrgb, "LINEAR_SRGB_COLOR_SPACE_TYPE")]
+
+[assembly: LocalizeEnum<EasingType>(EasingType.Linear, "LINEAR_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InSine, "IN_SINE_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutSine, "OUT_SINE_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutSine, "IN_OUT_SINE_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InQuad, "IN_QUAD_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutQuad, "OUT_QUAD_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutQuad, "IN_OUT_QUAD_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InCubic, "IN_CUBIC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutCubic, "OUT_CUBIC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutCubic, "IN_OUT_CUBIC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InQuart, "IN_QUART_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutQuart, "OUT_QUART_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutQuart, "IN_OUT_QUART_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InQuint, "IN_QUINT_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutQuint, "OUT_QUINT_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutQuint, "IN_OUT_QUINT_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InExpo, "IN_EXPO_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutExpo, "OUT_EXPO_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutExpo, "IN_OUT_EXPO_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InCirc, "IN_CIRC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutCirc, "OUT_CIRC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutCirc, "IN_OUT_CIRC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InBack, "IN_BACK_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutBack, "OUT_BACK_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutBack, "IN_OUT_BACK_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InElastic, "IN_ELASTIC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutElastic, "OUT_ELASTIC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutElastic, "IN_OUT_ELASTIC_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InBounce, "IN_BOUNCE_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.OutBounce, "OUT_BOUNCE_EASING_TYPE")]
+[assembly: LocalizeEnum<EasingType>(EasingType.InOutBounce, "IN_OUT_BOUNCE_EASING_TYPE")]
+
+[assembly: LocalizeEnum<RotationType>(RotationType.Degrees, "DEGREES_ROTATION_TYPE")]
+[assembly: LocalizeEnum<RotationType>(RotationType.Radians, "RADIANS_ROTATION_TYPE")]
+
+[assembly: LocalizeEnum<NoiseType>(NoiseType.TurbulencePerlin, "TURBULENCE_PERLIN_NOISE_TYPE")]
+[assembly: LocalizeEnum<NoiseType>(NoiseType.FractalPerlin, "FRACTAL_PERLIN_NOISE_TYPE")]
+[assembly: LocalizeEnum<NoiseType>(NoiseType.Voronoi, "VORONOI_NOISE_TYPE")]
+
+[assembly: LocalizeEnum<OutlineType>(OutlineType.Simple, "SIMPLE_OUTLINE_TYPE")]
+[assembly: LocalizeEnum<OutlineType>(OutlineType.Gaussian, "GAUSSIAN_OUTLINE_TYPE")]
+[assembly: LocalizeEnum<OutlineType>(OutlineType.PixelPerfect, "PIXEL_PERFECT_OUTLINE_TYPE")]
+
+[assembly: LocalizeEnum<VoronoiFeature>(VoronoiFeature.F1, "F1_VORONOI_FEATURE")]
+[assembly: LocalizeEnum<VoronoiFeature>(VoronoiFeature.F2, "F2_VORONOI_FEATURE")]
+[assembly: LocalizeEnum<VoronoiFeature>(VoronoiFeature.F2MinusF1, "F2_MINUS_F1_VORONOI_FEATURE")]

+ 5 - 1
src/PixiEditor/Models/Handlers/Toolbars/PaintBrushShape.cs

@@ -1,7 +1,11 @@
-namespace PixiEditor.Models.Handlers.Toolbars;
+using System.ComponentModel;
+
+namespace PixiEditor.Models.Handlers.Toolbars;
 
 public enum PaintBrushShape
 {
+    [Description("PAINT_BRUSH_SHAPE_CIRCLE")]
     Circle,
+    [Description("PAINT_BRUSH_SHAPE_SQUARE")]
     Square,
 }

+ 5 - 1
src/PixiEditor/Models/Tools/BrightnessMode.cs

@@ -1,7 +1,11 @@
-namespace PixiEditor.Models.Tools;
+using System.ComponentModel;
+
+namespace PixiEditor.Models.Tools;
 
 public enum BrightnessMode
 {
+    [Description("BRIGHTNESS_MODE_DEFAULT")]
     Default,
+    [Description("BRIGHTNESS_MODE_REPEAT")]
     Repeat
 }

+ 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.8")]
-[assembly: AssemblyFileVersion("2.0.1.8")]
+[assembly: AssemblyVersion("2.0.1.9")]
+[assembly: AssemblyFileVersion("2.0.1.9")]

+ 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"

+ 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,

+ 29 - 1
src/PixiEditor/ViewModels/Document/Nodes/NoiseNodeViewModel.cs

@@ -1,8 +1,36 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.UI.Common.Fonts;
 using PixiEditor.ViewModels.Nodes;
+using PixiEditor.ViewModels.Nodes.Properties;
 
 namespace PixiEditor.ViewModels.Document.Nodes;
 
 [NodeViewModel("NOISE_NODE", "IMAGE", PixiPerfectIcons.Noise)]
-internal class NoiseNodeViewModel : NodeViewModel<NoiseNode>;
+internal class NoiseNodeViewModel : NodeViewModel<NoiseNode>
+{
+    private GenericEnumPropertyViewModel Type { get; set; }
+    private GenericEnumPropertyViewModel VoronoiFeature { get; set; }
+    private NodePropertyViewModel Randomness { get; set; }
+    private NodePropertyViewModel AngleOffset { get; set; }
+
+    public override void OnInitialized()
+    {
+        Type = FindInputProperty("NoiseType") as GenericEnumPropertyViewModel;
+        VoronoiFeature = FindInputProperty("VoronoiFeature") as GenericEnumPropertyViewModel;
+        Randomness = FindInputProperty("Randomness");
+        AngleOffset = FindInputProperty("AngleOffset");
+
+        Type.ValueChanged += (_, _) => UpdateInputVisibility();
+        UpdateInputVisibility();
+    }
+
+    private void UpdateInputVisibility()
+    {
+        if (Type.Value is not NoiseType type)
+            return;
+        
+        Randomness.IsVisible = type == NoiseType.Voronoi;
+        VoronoiFeature.IsVisible = type == NoiseType.Voronoi;
+        AngleOffset.IsVisible = type == NoiseType.Voronoi;
+    }
+}

+ 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>
+{
+}

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/AnimationsViewModel.cs

@@ -189,7 +189,7 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
     {
         var activeDocument = Owner.DocumentManagerSubViewModel.ActiveDocument;
 
-        if (activeDocument is null)
+        if (activeDocument is null || info == default)
             return;
 
         if (!info.end)

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/ClipboardViewModel.cs

@@ -134,7 +134,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
             DataImage imageData =
                 (data == null
                     ? await ClipboardController.GetImagesFromClipboard()
-                    : ClipboardController.GetImage(new[] { data })).First();
+                    : ClipboardController.GetImage(new[] { new ImportedObject(data) }).Result).First();
             using var surface = imageData.Image;
 
             var bitmap = imageData.Image.ToWriteableBitmap();
@@ -398,7 +398,7 @@ internal class ClipboardViewModel : SubViewModel<ViewModelMain>
         var selectedNodes = doc.NodeGraph.AllNodes.Where(x => x.IsNodeSelected).Select(x => x.Id).ToArray();
         if (selectedNodes.Length == 0)
             return;
-
+        
         await ClipboardController.CopyNodes(selectedNodes, doc.Id);
 
         areNodesInClipboard = true;

+ 1 - 1
src/PixiEditor/ViewModels/SubViewModels/FileViewModel.cs

@@ -235,7 +235,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
             {
                 if (File.Exists(dataImage.Name))
                 {
-                    OpenRegularImage(dataImage.Image, null);
+                    OpenRegularImage(dataImage.Image, dataImage.Name);
                     continue;
                 }
 

+ 3 - 1
src/PixiEditor/ViewModels/SubViewModels/LayersViewModel.cs

@@ -235,6 +235,9 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         AnalyticsTrack = true)]
     public void DuplicateMember()
     {
+        if (Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember == null)
+            return;
+
         var member = Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember;
 
         member.Document.Operations.DuplicateMember(member.Id);
@@ -379,7 +382,6 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         nameof(ViewModelMain.DocumentManagerSubViewModel.ActiveDocument),
         nameof(ViewModelMain.DocumentManagerSubViewModel.ActiveDocument.SelectedStructureMember),
         nameof(ViewModelMain.DocumentManagerSubViewModel.ActiveDocument.SelectedStructureMember.HasMaskBindable))]
-
     public bool ActiveMemberHasApplyableMask() =>
         (Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember?.HasMaskBindable ?? false)
         && Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember is IRasterLayerHandler;

+ 8 - 4
src/PixiEditor/Views/Dialogs/ExportFilePopup.axaml.cs

@@ -247,10 +247,14 @@ internal partial class ExportFilePopup : PixiEditorPopup
     protected override void OnClosing(WindowClosingEventArgs e)
     {
         base.OnClosing(e);
-        videoPreviewTimer.Stop();
-        videoPreviewTimer.Tick -= OnVideoPreviewTimerOnTick;
-        videoPreviewTimer = null;
-        cancellationTokenSource.Cancel();
+        if (videoPreviewTimer != null)
+        {
+            videoPreviewTimer.Stop();
+            videoPreviewTimer.Tick -= OnVideoPreviewTimerOnTick;
+            videoPreviewTimer = null;
+        }
+
+        cancellationTokenSource?.Cancel();
 
         ExportPreview?.Dispose();
 

+ 2 - 1
src/PixiEditor/Views/Layers/LayersManager.axaml.cs

@@ -181,7 +181,8 @@ internal partial class LayersManager : UserControl
             e.Handled = true;
         }
 
-        if (ClipboardController.TryPaste(ActiveDocument, ManagerViewModel, new[] { (IDataObject)e.Data }, true))
+        // Imported object doesn't do any async operations so .Result is safe to use here.
+        if (ClipboardController.TryPaste(ActiveDocument, ManagerViewModel, new[] { new ImportedObject((IDataObject)e.Data) }, true).Result)
         {
             e.Handled = true;
         }

+ 5 - 9
src/PixiEditor/Views/Main/CommandSearch/CommandSearchControl.axaml.cs

@@ -184,11 +184,11 @@ internal partial class CommandSearchControl : UserControl, INotifyPropertyChange
         }
         else if (e.Key is Key.Down or Key.PageDown)
         {
-            MoveSelection(1);
+            MoveSelection(NextToDirection.Forwards);
         }
         else if (e.Key is Key.Up or Key.PageUp)
         {
-            MoveSelection(-1);
+            MoveSelection(NextToDirection.Backwards);
         }
         else if (e.Key == Key.Escape ||
                  CommandController.Current.Commands["PixiEditor.Search.Toggle"].Shortcut
@@ -266,22 +266,18 @@ internal partial class CommandSearchControl : UserControl, INotifyPropertyChange
         }
     }
 
-    private void MoveSelection(int delta)
+    private void MoveSelection(NextToDirection direction)
     {
-        if (delta == 0)
-            return;
         if (SelectedResult is null)
         {
             SelectedResult = Results.FirstOrDefault(x => x.CanExecute);
             return;
         }
 
-        int newIndex = Results.IndexOf(SelectedResult) + delta;
+        var newIndex = Results.IndexOf(SelectedResult) + (int)direction;
         newIndex = (newIndex % Results.Count + Results.Count) % Results.Count;
 
-        SelectedResult = delta > 0
-            ? Results.IndexOrNext(x => x.CanExecute, newIndex)
-            : Results.IndexOrPrevious(x => x.CanExecute, newIndex);
+        SelectedResult = Results.IndexOrNextInDirection(x => x.CanExecute, newIndex, direction);
 
         newIndex = Results.IndexOf(SelectedResult);
         itemscontrol.ContainerFromIndex(newIndex)?.BringIntoView();

+ 6 - 2
src/PixiEditor/Views/MainView.axaml.cs

@@ -75,9 +75,13 @@ public partial class MainView : UserControl
             return;
         }
 
-        if (fileDropList is { Length: > 0 } && Importer.IsSupportedFile(fileDropList[0].Path.LocalPath))
+        if (fileDropList is { Length: > 0 })
         {
-            Context.FileSubViewModel.OpenFromPath(fileDropList[0].Path.LocalPath);
+            foreach (var item in fileDropList)
+            {
+                Importer.IsSupportedFile(item.Path.LocalPath);
+                Context.FileSubViewModel.OpenFromPath(item.Path.LocalPath);
+            }
         }
     }
 

+ 82 - 8
src/PixiEditor/Views/Nodes/NodePicker.cs

@@ -5,7 +5,10 @@ using Avalonia.Controls;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Primitives;
 using Avalonia.Input;
+using Avalonia.Interactivity;
 using Avalonia.Threading;
+using Avalonia.VisualTree;
+using PixiEditor.Helpers.Extensions;
 using PixiEditor.Helpers.Nodes;
 using PixiEditor.ViewModels.Nodes;
 using PixiEditor.Views.Input;
@@ -43,6 +46,15 @@ public partial class NodePicker : TemplatedControl
         set => SetValue(SelectedCategoryProperty, value);
     }
 
+    public static readonly StyledProperty<NodeTypeInfo> SelectedNodeProperty =
+        AvaloniaProperty.Register<NodePicker, NodeTypeInfo>(nameof(SelectedNode));
+
+    public NodeTypeInfo? SelectedNode
+    {
+        get => GetValue(SelectedNodeProperty);
+        set => SetValue(SelectedNodeProperty, value);
+    }
+
     public ObservableCollection<NodeTypeInfo> AllNodeTypeInfos
     {
         get => GetValue(AllNodeTypeInfosProperty);
@@ -113,6 +125,7 @@ public partial class NodePicker : TemplatedControl
     {
         _inputBox = e.NameScope.Find<InputBox>("PART_InputBox");
 
+        _inputBox.Loaded += (_, _) => _inputBox.SelectAll();
         _inputBox.KeyDown += OnInputBoxKeyDown;
 
         _itemsControl = e.NameScope.Find<ItemsControl>("PART_NodeList");
@@ -158,6 +171,8 @@ public partial class NodePicker : TemplatedControl
             return;
         }
 
+        nodePicker.SelectedNode = null;
+        
         if (NodeAbbreviation.IsAbbreviation(nodePicker.SearchQuery, out var abbreviationName))
         {
             nodePicker.FilteredNodeGroups = nodePicker.NodeTypeGroupsFromQuery(abbreviationName);
@@ -235,8 +250,22 @@ public partial class NodePicker : TemplatedControl
 
     private void OnInputBoxKeyDown(object? sender, KeyEventArgs e)
     {
-        if (e.Key != Key.Enter)
+        switch (e.Key)
         {
+            case Key.Enter:
+                HandleEnterDown(sender, e);
+                return;
+            case Key.Down or Key.Up:
+                HandleKeyUpDown(sender, e); 
+                return;
+        }
+    }
+
+    private void HandleEnterDown(object? sender, KeyEventArgs e)
+    {
+        if (SelectedNode != null)
+        {
+            SelectNodeCommand.Execute(SelectedNode);
             return;
         }
 
@@ -247,17 +276,62 @@ public partial class NodePicker : TemplatedControl
             return;
         }
 
-        if (nodes == null && FilteredNodeGroups.Count > 0)
+        foreach (var node in nodes)
         {
-            SelectNodeCommand.Execute(FilteredNodeGroups[0]);
+            SelectNodeCommand.Execute(node);
         }
-        else
+    }
+
+    private void HandleKeyUpDown(object? sender, KeyEventArgs e)
+    {
+        if (SelectedNode == null)
         {
-            foreach (var node in nodes)
-            {
-                SelectNodeCommand.Execute(node);
-            }
+            SelectedNode = e.Key == Key.Down
+                ? FilteredNodeGroups.FirstOrDefault()?.NodeTypes.FirstOrDefault()
+                : FilteredNodeGroups.LastOrDefault()?.NodeTypes.LastOrDefault();
+                
+            return;
+        }
+
+        var direction = e.Key == Key.Down ? NextToDirection.Forwards : NextToDirection.Backwards;
+        SelectedNode = GetNodeNextTo(FilteredNodeGroups, SelectedNode, direction, out var group);
+
+        var container = _itemsControl.ContainerFromItem(group);
+        var buttonList = container.FindDescendantOfType<ItemsControl>();
+        
+        var button = buttonList.ContainerFromItem(SelectedNode);
+
+        const double padding = 2.6;
+        const double paddingHeight = padding * 2 + 1;
+        
+        // Bring Button above/below also into view
+        button.BringIntoView(new Rect(0, button.Bounds.Height * -padding, button.Bounds.Width, button.Bounds.Height * paddingHeight));
+    }
+
+    private static NodeTypeInfo? GetNodeNextTo(ObservableCollection<NodeTypeGroup> groups, NodeTypeInfo node, NextToDirection direction, out NodeTypeGroup group)
+    {
+        var currentGroup = groups.FirstOrDefault(x => x.NodeTypes.Contains(node));
+
+        group = currentGroup;
+        if (currentGroup == null)
+            return null;
+        
+        var indexInGroup = currentGroup.NodeTypes.IndexOf(node);
+        var groupIndex = groups.IndexOf(currentGroup);
+
+        if (direction == NextToDirection.Backwards && indexInGroup == 0)
+        {
+            group = groups.WrapPreviousBeforeIndex(groupIndex);
+            return group.NodeTypes.Last();
         }
+
+        if (direction == NextToDirection.Forwards && indexInGroup == currentGroup.NodeTypes.Count - 1)
+        {
+            group = groups.WrapNextAfterIndex(groupIndex);
+            return group.NodeTypes.First();
+        }
+
+        return currentGroup.NodeTypes[indexInGroup + (int)direction];
     }
 
     private static bool SearchComparer(NodeTypeInfo x, string lookFor) =>

+ 2 - 2
src/PixiEditor/Views/Nodes/Properties/NodePropertyView.cs

@@ -91,12 +91,12 @@ public abstract class NodePropertyView : UserControl
 
     protected void HideSocket(bool hideInputSocket, bool hideOutputSocket)
     {
-        if (hideInputSocket)
+        if (hideInputSocket && InputSocket is not null)
         {
             InputSocket.IsVisible = false;
         }
 
-        if (hideOutputSocket)
+        if (hideOutputSocket && OutputSocket is not null)
         {
             OutputSocket.IsVisible = false;
         }

+ 8 - 1
src/PixiEditor/Views/Overlays/BrushShapeOverlay/BrushShape.cs

@@ -1,9 +1,16 @@
-namespace PixiEditor.Views.Overlays.BrushShapeOverlay;
+using System.ComponentModel;
+
+namespace PixiEditor.Views.Overlays.BrushShapeOverlay;
 internal enum BrushShape
 {
+    [Description("BRUSH_SHAPE_HIDDEN")]
     Hidden,
+    [Description("BRUSH_SHAPE_PIXEL")]
     Pixel,
+    [Description("BRUSH_SHAPE_SQUARE")]
     Square,
+    [Description("BRUSH_SHAPE_CIRCLE_PIXELATED")]
     CirclePixelated,
+    [Description("BRUSH_SHAPE_CIRCLE_SMOOTH")]
     CircleSmooth
 }

+ 10 - 2
src/PixiEditor/Views/Overlays/Handles/Handle.cs

@@ -1,4 +1,5 @@
-using Avalonia;
+using System;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.Shapes;
 using Avalonia.Input;
@@ -76,7 +77,14 @@ public abstract class Handle : IHandle
         {
             if (shape is string path)
             {
-                return new PathVectorData(VectorPath.FromSvgPath(path));
+                try
+                {
+                    return new PathVectorData(VectorPath.FromSvgPath(path));
+                }
+                catch (Exception ex)
+                {
+                    return new PathVectorData(VectorPath.FromSvgPath("M 0 0 L 1 0 M 0 0 L 0 1"));
+                }
             }
 
             if (shape is VectorPathResource resource)

+ 3 - 4
src/PixiEditor/Views/Tools/ToolSettings/Settings/EnumSettingView.axaml

@@ -4,9 +4,8 @@
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:settings="clr-namespace:PixiEditor.ViewModels.Tools.ToolSettings.Settings"
              xmlns:enums="clr-namespace:PixiEditor.ChangeableDocument.Enums;assembly=PixiEditor.ChangeableDocument"
-             xmlns:ui="clr-namespace:PixiEditor.Extensions.UI;assembly=PixiEditor.Extensions"
-             xmlns:helpers="clr-namespace:PixiEditor.Helpers"
              xmlns:localization="clr-namespace:PixiEditor.UI.Common.Localization;assembly=PixiEditor.UI.Common"
+             xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:Class="PixiEditor.Views.Tools.ToolSettings.Settings.EnumSettingView">
     <Design.DataContext>
@@ -20,12 +19,12 @@
         <ComboBox.ItemContainerTheme>
             <ControlTheme TargetType="ComboBoxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
                 <Setter Property="Tag" Value="{Binding .}"/>
-                <Setter Property="(localization:Translator.Key)" Value="{Binding ., Converter={helpers:EnumDescriptionConverter}}"/>
+                <Setter Property="(localization:Translator.Key)" Value="{Binding ., Converter={converters:EnumToLocalizedStringConverter}}"/>
             </ControlTheme>
         </ComboBox.ItemContainerTheme>
        <ComboBox.ItemTemplate>
            <DataTemplate>
-               <TextBlock localization:Translator.Key="{Binding ., Converter={helpers:EnumDescriptionConverter}}"/>
+               <TextBlock localization:Translator.Key="{Binding ., Converter={converters:EnumToLocalizedStringConverter}}"/>
            </DataTemplate>
        </ComboBox.ItemTemplate>
     </ComboBox>

+ 1 - 1
tests/Directory.Build.props

@@ -1,7 +1,7 @@
 <Project>
     <PropertyGroup>
         <CodeAnalysisRuleSet>../Custom.ruleset</CodeAnalysisRuleSet>
-		<AvaloniaVersion>11.3.3</AvaloniaVersion>
+		<AvaloniaVersion>11.3.0</AvaloniaVersion>
     </PropertyGroup>
     <ItemGroup>
         <PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />