Jelajahi Sumber

Merge pull request #599 from PixiEditor/master

1.2.2.0
Krzysztof Krysiński 1 tahun lalu
induk
melakukan
30c4bfb823
72 mengubah file dengan 1710 tambahan dan 900 penghapusan
  1. 56 21
      LICENSE
  2. 1 1
      README.md
  3. 1 1
      src/ChunkyImageLib/DataHolders/AffectedArea.cs
  4. 3 3
      src/ChunkyImageLib/DataHolders/ColorBounds.cs
  5. 1 1
      src/ChunkyImageLib/Operations/OperationHelper.cs
  6. 6 1
      src/ChunkyImageLib/Surface.cs
  7. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs
  8. 0 2
      src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectionToMask_Change.cs
  9. 6 2
      src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelectedArea_UpdateableChange.cs
  10. 1 7
      src/PixiEditor.ChangeableDocument/Changes/Root/ClipCanvas_Change.cs
  11. 3 3
      src/PixiEditor.ChangeableDocument/Changes/Root/ResizeBasedChangeBase.cs
  12. 1 1
      src/PixiEditor.ChangeableDocument/Changes/Structure/DuplicateLayer_Change.cs
  13. 6 5
      src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs
  14. 2 2
      src/PixiEditor.DrawingApi.Core/Bridge/Operations/IImageImplementation.cs
  15. 1 1
      src/PixiEditor.DrawingApi.Core/Numerics/RectI.cs
  16. 2 2
      src/PixiEditor.DrawingApi.Core/Surface/ImageData/Image.cs
  17. 5 0
      src/PixiEditor.DrawingApi.Core/Surface/Pixmap.cs
  18. 6 2
      src/PixiEditor.DrawingApi.Skia/Implementations/SkiaImageImplementation.cs
  19. 1 1
      src/PixiEditor.DrawingApi.Skia/Implementations/SkiaPixmapImplementation.cs
  20. 5 0
      src/PixiEditor.Platform.MSStore/MicrosoftStorePlatform.cs
  21. 5 0
      src/PixiEditor.Platform.Standalone/StandalonePlatform.cs
  22. 6 2
      src/PixiEditor.Platform.Steam/OverlayHandler.cs
  23. 3 1
      src/PixiEditor.Platform.Steam/SteamAdditionalContentProvider.cs
  24. 6 0
      src/PixiEditor.Platform.Steam/SteamPlatform.cs
  25. 1 0
      src/PixiEditor.Platform/IPlatform.cs
  26. 1 1
      src/PixiEditor.Zoombox/Operations/ZoomDragOperation.cs
  27. 1 1
      src/PixiEditor.Zoombox/Zoombox.xaml.cs
  28. 38 14
      src/PixiEditor/App.xaml.cs
  29. 4 1
      src/PixiEditor/Data/Localization/Languages/ar.json
  30. 5 2
      src/PixiEditor/Data/Localization/Languages/cs.json
  31. 4 1
      src/PixiEditor/Data/Localization/Languages/de.json
  32. 4 1
      src/PixiEditor/Data/Localization/Languages/en.json
  33. 4 1
      src/PixiEditor/Data/Localization/Languages/it.json
  34. 4 1
      src/PixiEditor/Data/Localization/Languages/pl.json
  35. 22 19
      src/PixiEditor/Data/Localization/Languages/pt-br.json
  36. 38 35
      src/PixiEditor/Data/Localization/Languages/ru.json
  37. 585 582
      src/PixiEditor/Data/Localization/Languages/tr.json
  38. 4 1
      src/PixiEditor/Data/Localization/Languages/uk.json
  39. 19 16
      src/PixiEditor/Data/Localization/Languages/zh.json
  40. 9 9
      src/PixiEditor/Data/Localization/LocalizationData.json
  41. 7 2
      src/PixiEditor/Helpers/Converters/ReciprocalConverter.cs
  42. 13 3
      src/PixiEditor/Helpers/CrashHelper.cs
  43. 5 1
      src/PixiEditor/Helpers/ProcessHelper.cs
  44. 2 2
      src/PixiEditor/Models/AppExtensions/ExtensionLoader.cs
  45. 3 0
      src/PixiEditor/Models/Commands/CommandController.cs
  46. 43 0
      src/PixiEditor/Models/Commands/CommandLog/CommandLog.cs
  47. 19 0
      src/PixiEditor/Models/Commands/CommandLog/CommandLogEntry.cs
  48. 17 2
      src/PixiEditor/Models/Commands/CommandMethods.cs
  49. 99 21
      src/PixiEditor/Models/Controllers/MouseUpdateController.cs
  50. 263 39
      src/PixiEditor/Models/DataHolders/CrashReport.cs
  51. 5 1
      src/PixiEditor/Models/Dialogs/OptionsDialog.cs
  52. 3 1
      src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs
  53. 37 14
      src/PixiEditor/Models/IO/Exporter.cs
  54. 14 3
      src/PixiEditor/Models/IO/Importer.cs
  55. 2 2
      src/PixiEditor/Models/Rendering/CanvasUpdater.cs
  56. 1 1
      src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs
  57. 2 2
      src/PixiEditor/Properties/AssemblyInfo.cs
  58. 24 2
      src/PixiEditor/ViewModels/CrashReportViewModel.cs
  59. 6 1
      src/PixiEditor/ViewModels/SubViewModels/Document/TransformOverlays/DocumentTransformViewModel.cs
  60. 19 13
      src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs
  61. 2 2
      src/PixiEditor/ViewModels/SubViewModels/Main/MiscViewModel.cs
  62. 5 0
      src/PixiEditor/ViewModels/SubViewModels/Main/UpdateViewModel.cs
  63. 19 0
      src/PixiEditor/ViewModels/ViewModelMain.cs
  64. 14 3
      src/PixiEditor/Views/Dialogs/HelloTherePopup.xaml.cs
  65. 1 0
      src/PixiEditor/Views/Dialogs/OptionsPopup.xaml.cs
  66. 27 0
      src/PixiEditor/Views/LoadingWindow.xaml
  67. 43 0
      src/PixiEditor/Views/LoadingWindow.xaml.cs
  68. 0 17
      src/PixiEditor/Views/MainWindow.xaml
  69. 47 20
      src/PixiEditor/Views/MainWindow.xaml.cs
  70. 3 3
      src/PixiEditor/Views/UserControls/Overlays/BrushShapeOverlay/BrushShapeOverlay.cs
  71. 17 0
      src/PixiEditor/Views/UserControls/SteamOverlay.xaml
  72. 77 0
      src/PixiEditor/Views/UserControls/SteamOverlay.xaml.cs

+ 56 - 21
LICENSE

@@ -1,21 +1,56 @@
-MIT License
-
-Copyright (c) PixiEditor Organization
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+GNU LESSER GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright © 2007 Free Software Foundation, Inc. <https://fsf.org/>
+
+Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
+
+This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.
+
+0. Additional Definitions.
+As used herein, “this License” refers to version 3 of the GNU Lesser General Public License, and the “GNU GPL” refers to version 3 of the GNU General Public License.
+
+“The Library” refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.
+
+An “Application” is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.
+
+A “Combined Work” is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the “Linked Version”.
+
+The “Minimal Corresponding Source” for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.
+
+The “Corresponding Application Code” for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.
+
+1. Exception to Section 3 of the GNU GPL.
+You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.
+
+2. Conveying Modified Versions.
+If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:
+
+a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or
+b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy.
+3. Object Code Incorporating Material from Library Header Files.
+The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:
+
+a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License.
+b) Accompany the object code with a copy of the GNU GPL and this license document.
+4. Combined Works.
+You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:
+
+a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.
+b) Accompany the Combined Work with a copy of the GNU GPL and this license document.
+c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.
+d) Do one of the following:
+0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.
+1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version.
+e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)
+5. Combined Libraries.
+You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:
+
+a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License.
+b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work.
+6. Revised Versions of the GNU Lesser General Public License.
+The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.
+
+If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

+ 1 - 1
README.md

@@ -98,4 +98,4 @@ Please read [CONTRIBUTING.md](https://github.com/flabbet/PixiEditor/blob/master/
 
 ## License
 
-This project is licensed under the MIT License - see the [LICENSE.md](https://github.com/flabbet/PixiEditor/blob/master/LICENSE) - file for details
+This project is licensed under the LGPLv3 License - see the [LICENSE.md](https://github.com/flabbet/PixiEditor/blob/master/LICENSE) - file for details

+ 1 - 1
src/ChunkyImageLib/DataHolders/AffectedArea.cs

@@ -19,7 +19,7 @@ public struct AffectedArea
     public HashSet<VecI> Chunks { get; set; }
 
     /// <summary>
-    /// A rectangle in global full-scale coordinat
+    /// A rectangle in global full-scale coordinates
     /// </summary>
     public RectI? GlobalArea { get; set; }
 

+ 3 - 3
src/ChunkyImageLib/DataHolders/ColorBounds.cs

@@ -65,10 +65,10 @@ public struct ColorBounds
 
     public bool IsWithinBounds(Color toCompare)
     {
-        float r = toCompare.R / 255f;
-        float g = toCompare.G / 255f;
-        float b = toCompare.B / 255f;
         float a = toCompare.A / 255f;
+        float r = (toCompare.R / 255f) * a;
+        float g = (toCompare.G / 255f) * a;
+        float b = (toCompare.B / 255f) * a;
         
         if (r < LowerR || r > UpperR)
             return false;

+ 1 - 1
src/ChunkyImageLib/Operations/OperationHelper.cs

@@ -234,7 +234,7 @@ public static class OperationHelper
 
     public static HashSet<VecI> FindChunksTouchingRectangle(RectI rect, int chunkSize)
     {
-        if (rect.Width > chunkSize * 40 * 20 || rect.Height > chunkSize * 40 * 20)
+        if (rect.Width > chunkSize * 40 * 20 || rect.Height > chunkSize * 40 * 20 || rect.IsZeroOrNegativeArea)
             return new HashSet<VecI>();
 
         VecI min = GetChunkPos(rect.TopLeft, chunkSize);

+ 6 - 1
src/ChunkyImageLib/Surface.cs

@@ -1,4 +1,5 @@
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
@@ -64,6 +65,8 @@ public class Surface : IDisposable
     public static Surface Load(byte[] encoded)
     {
         using var image = Image.FromEncodedData(encoded);
+        if (image is null)
+            throw new ArgumentException($"The passed byte array does not contain a valid image");
 
         var surface = new Surface(new VecI(image.Width, image.Height));
         surface.DrawingSurface.Canvas.DrawImage(image, 0, 0);
@@ -129,6 +132,7 @@ public class Surface : IDisposable
         return true;
     }
 
+#if DEBUG
     public void SaveToDesktop(string filename = "savedSurface.png")
     {
         using var final = DrawingSurface.Create(new ImageInfo(Size.X, Size.Y, ColorType.Rgba8888, AlphaType.Premul, ColorSpace.CreateSrgb()));
@@ -140,6 +144,7 @@ public class Surface : IDisposable
             png.SaveTo(stream);
         }
     }
+#endif
 
     private DrawingSurface CreateDrawingSurface()
     {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Drawing/FloodFill/FloodFillHelper.cs

@@ -53,7 +53,7 @@ public static class FloodFillHelper
             static (EmptyChunk _) => Colors.Transparent
         );
 
-        if ((colorToReplace.A == 0 && drawingColor.A == 0) || colorToReplace == drawingColor)
+        if ((drawingColor.A == 0) || colorToReplace == drawingColor)
             return new();
 
         RectI globalSelectionBounds = (RectI?)selection?.TightBounds ?? new RectI(VecI.Zero, document.Size);

+ 0 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectionToMask_Change.cs

@@ -48,8 +48,6 @@ internal class SelectionToMask_Change : Change
         image.SetBlendMode(blendMode);
 
         var selectionImage = FloodFillHelper.FillSelection(target, selection!);
-        
-        selectionImage.SaveToDesktop();
 
         image.EnqueueDrawImage(new VecI(0, 0), selectionImage);
         

+ 6 - 2
src/PixiEditor.ChangeableDocument/Changes/Drawing/TransformSelectedArea_UpdateableChange.cs

@@ -177,9 +177,13 @@ internal class TransformSelectedArea_UpdateableChange : UpdateableChange
     {
         if (hasEnqueudImages)
             throw new InvalidOperationException("Attempted to dispose the change while it's internally stored image is still used enqueued in some ChunkyImage. Most likely someone tried to dispose a change after ApplyTemporarily was called but before the subsequent call to Apply. Don't do that.");
-        foreach (var (_, (image, _)) in images!)
+
+        if (images is not null)
         {
-            image.Dispose();
+            foreach (var (_, (image, _)) in images)
+            {
+                image.Dispose();
+            }
         }
 
         if (savedChunks is not null)

+ 1 - 7
src/PixiEditor.ChangeableDocument/Changes/Root/ClipCanvas_Change.cs

@@ -24,7 +24,7 @@ internal class ClipCanvas_Change : ResizeBasedChangeBase
             }
         });
 
-        if (!bounds.HasValue)
+        if (!bounds.HasValue || bounds.Value.IsZeroOrNegativeArea || bounds.Value == new RectI(VecI.Zero, target.Size))
         {
             ignoreInUndo = true;
             return new None();
@@ -48,12 +48,6 @@ internal class ClipCanvas_Change : ResizeBasedChangeBase
             
             Resize(member.Mask, member.GuidValue, newBounds.Size, -newBounds.Pos, deletedMaskChunks);
         });
-        
-        if (newBounds.IsZeroOrNegativeArea)
-        {
-            ignoreInUndo = true;
-            return new None();
-        }
 
         ignoreInUndo = false;
         return new Size_ChangeInfo(newBounds.Size, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changes/Root/ResizeBasedChangeBase.cs

@@ -19,6 +19,9 @@ internal abstract class ResizeBasedChangeBase : Change
         return true;
     }
     
+    /// <summary>
+    /// Notice: this commits image changes, you won't have a chance to revert or set ignoreInUndo to true
+    /// </summary>
     protected virtual void Resize(ChunkyImage img, Guid memberGuid, VecI size, VecI offset, Dictionary<Guid, CommittedChunkStorage> deletedChunksDict)
     {
         img.EnqueueResize(size);
@@ -31,9 +34,6 @@ internal abstract class ResizeBasedChangeBase : Change
     
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
-        if (target.Size == _originalSize)
-            return new None();
-
         target.Size = _originalSize;
         target.ForEveryMember((member) =>
         {

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

@@ -15,6 +15,7 @@ internal class DuplicateLayer_Change : Change
     {
         if (!target.TryFindMember<Layer>(layerGuid, out Layer? layer))
             return false;
+        duplicateGuid = Guid.NewGuid();
         return true;
     }
 
@@ -23,7 +24,6 @@ internal class DuplicateLayer_Change : Change
         (Layer existingLayer, Folder parent) = ((Layer, Folder))target.FindChildAndParentOrThrow(layerGuid);
 
         Layer clone = existingLayer.Clone();
-        duplicateGuid = Guid.NewGuid();
         clone.GuidValue = duplicateGuid;
 
         int index = parent.Children.IndexOf(existingLayer);

+ 6 - 5
src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs

@@ -247,22 +247,23 @@ public static class ChunkRenderer
         OneOf<All, HashSet<Guid>> membersToMerge,
         RectI? transformedClippingRect)
     {
-        if (folder.Children.Count == 0)
+        var folderChildren = folder.Children;
+        if (folderChildren.Count == 0)
             return new EmptyChunk();
 
         Chunk targetChunk = Chunk.Create(resolution);
         targetChunk.Surface.DrawingSurface.Canvas.Clear();
 
         OneOf<FilledChunk, EmptyChunk, Chunk> clippingChunk = new FilledChunk();
-        for (int i = 0; i < folder.Children.Count; i++)
+        for (int i = 0; i < folderChildren.Count; i++)
         {
-            var child = folder.Children[i];
+            var child = folderChildren[i];
 
             // next child might use clip to member below in which case we need to save the clip image
             bool needToSaveClippingChunk =
-                i < folder.Children.Count - 1 &&
+                i < folderChildren.Count - 1 &&
                 !child.ClipToMemberBelow &&
-                folder.Children[i + 1].ClipToMemberBelow;
+                folderChildren[i + 1].ClipToMemberBelow;
 
             // if the current member doesn't need a clip, get rid of it
             if (!child.ClipToMemberBelow && !clippingChunk.IsT0)

+ 2 - 2
src/PixiEditor.DrawingApi.Core/Bridge/Operations/IImageImplementation.cs

@@ -8,8 +8,8 @@ namespace PixiEditor.DrawingApi.Core.Bridge.Operations
     {
         public Image Snapshot(DrawingSurface drawingSurface);
         public void DisposeImage(Image image);
-        public Image FromEncodedData(string path);
-        public Image FromEncodedData(byte[] dataBytes);
+        public Image? FromEncodedData(string path);
+        public Image? FromEncodedData(byte[] dataBytes);
         public void GetColorShifts(ref int platformColorAlphaShift, ref int platformColorRedShift, ref int platformColorGreenShift, ref int platformColorBlueShift);
         public ImgData Encode(Image image);
         public int GetWidth(IntPtr objectPointer);

+ 1 - 1
src/PixiEditor.DrawingApi.Core/Numerics/RectI.cs

@@ -80,7 +80,7 @@ public struct RectI : IEquatable<RectI>
     public int Width { readonly get => right - left; set => right = left + value; }
     public int Height { readonly get => bottom - top; set => bottom = top + value; }
     public readonly bool IsZeroArea => left == right || top == bottom;
-    public readonly bool IsZeroOrNegativeArea => left >= right || top >= bottom;
+    public readonly bool IsZeroOrNegativeArea => left >= right || top >= bottom || Width < 0 || Height < 0; // checking Width and Height too as they can overflow in large rectangles 
 
     public RectI()
     {

+ 2 - 2
src/PixiEditor.DrawingApi.Core/Surface/ImageData/Image.cs

@@ -26,12 +26,12 @@ namespace PixiEditor.DrawingApi.Core.Surface.ImageData
             DrawingBackendApi.Current.ImageImplementation.DisposeImage(this);
         }
 
-        public static Image FromEncodedData(string path)
+        public static Image? FromEncodedData(string path)
         {
             return DrawingBackendApi.Current.ImageImplementation.FromEncodedData(path);
         }
         
-        public static Image FromEncodedData(byte[] dataBytes)
+        public static Image? FromEncodedData(byte[] dataBytes)
         {
             return DrawingBackendApi.Current.ImageImplementation.FromEncodedData(dataBytes);
         }

+ 5 - 0
src/PixiEditor.DrawingApi.Core/Surface/Pixmap.cs

@@ -9,6 +9,11 @@ public class Pixmap : NativeObject
     internal Pixmap(IntPtr objPtr) : base(objPtr)
     {
     }
+
+    public static Pixmap InternalCreateFromExistingPointer(IntPtr objPointer)
+    {
+        return new Pixmap(objPointer);
+    }
     
     public Pixmap(ImageInfo imgInfo, IntPtr dataPtr) : base(dataPtr)
     {

+ 6 - 2
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaImageImplementation.cs

@@ -31,9 +31,11 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             return new Image(snapshot.Handle);
         }
         
-        public Image FromEncodedData(byte[] dataBytes)
+        public Image? FromEncodedData(byte[] dataBytes)
         {
             SKImage img = SKImage.FromEncodedData(dataBytes);
+            if (img is null)
+                return null;
             ManagedInstances[img.Handle] = img;
             
             return new Image(img.Handle);
@@ -45,9 +47,11 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
             ManagedInstances.TryRemove(image.ObjectPointer, out _);
         }
 
-        public Image FromEncodedData(string path)
+        public Image? FromEncodedData(string path)
         {
             var nativeImg = SKImage.FromEncodedData(path);
+            if (nativeImg is null)
+                return null;
             ManagedInstances[nativeImg.Handle] = nativeImg;
             return new Image(nativeImg.Handle);
         }

+ 1 - 1
src/PixiEditor.DrawingApi.Skia/Implementations/SkiaPixmapImplementation.cs

@@ -57,7 +57,7 @@ namespace PixiEditor.DrawingApi.Skia.Implementations
         public Pixmap CreateFrom(SKPixmap pixmap)
         {
             ManagedInstances[pixmap.Handle] = pixmap;
-            return new Pixmap(pixmap.Info.ToImageInfo(colorSpaceImplementation), pixmap.GetPixels());
+            return Pixmap.InternalCreateFromExistingPointer(pixmap.Handle);
         }
     }
 }

+ 5 - 0
src/PixiEditor.Platform.MSStore/MicrosoftStorePlatform.cs

@@ -10,5 +10,10 @@ public sealed class MicrosoftStorePlatform : IPlatform
         return true;
     }
 
+    public void Update()
+    {
+
+    }
+
     public IAdditionalContentProvider? AdditionalContentProvider { get; } = new MSAdditionalContentProvider();
 }

+ 5 - 0
src/PixiEditor.Platform.Standalone/StandalonePlatform.cs

@@ -10,5 +10,10 @@ public sealed class StandalonePlatform : IPlatform
         return true;
     }
 
+    public void Update()
+    {
+
+    }
+
     public IAdditionalContentProvider? AdditionalContentProvider { get; } = new StandaloneAdditionalContentProvider();
 }

+ 6 - 2
src/PixiEditor.Platform.Steam/OverlayHandler.cs

@@ -7,6 +7,8 @@ public class SteamOverlayHandler
     public event Action<bool> ActivateRefreshingElement;
     protected Callback<GameOverlayActivated_t> overlayActivated;
 
+    private bool _isOverlayActive;
+
     public SteamOverlayHandler()
     {
         overlayActivated = Callback<GameOverlayActivated_t>.Create(OnOverlayActivated);
@@ -15,18 +17,20 @@ public class SteamOverlayHandler
 
     private void InitStartingRefresh()
     {
-        System.Timers.Timer timer = new(10000);
+        System.Timers.Timer timer = new(11000);
         timer.Elapsed += (sender, args) =>
         {
-            if (SteamUtils.IsOverlayEnabled()) return;
+            if (_isOverlayActive) return;
 
             ActivateRefreshingElement?.Invoke(false);
+            timer.Stop();
         };
         timer.Start();
     }
 
     private void OnOverlayActivated(GameOverlayActivated_t param)
     {
+        _isOverlayActive = param.m_bActive == 1;
         ActivateRefreshingElement?.Invoke(param.m_bActive == 1);
     }
 }

+ 3 - 1
src/PixiEditor.Platform.Steam/SteamAdditionalContentProvider.cs

@@ -11,10 +11,12 @@ public sealed class SteamAdditionalContentProvider : IAdditionalContentProvider
 
     public bool IsContentInstalled(AdditionalContentProduct product)
     {
+        if(!SteamAPI.IsSteamRunning()) return false;
         if(!PlatformHasContent(product)) return false;
 
         AppId_t appId = productIds[product];
-        return SteamApps.BIsDlcInstalled(appId);
+        bool installed = SteamApps.BIsDlcInstalled(appId);
+        return installed;
     }
 
     public bool PlatformHasContent(AdditionalContentProduct product)

+ 6 - 0
src/PixiEditor.Platform.Steam/SteamPlatform.cs

@@ -1,4 +1,5 @@
 using Steamworks;
+using Timer = System.Timers.Timer;
 
 namespace PixiEditor.Platform.Steam;
 
@@ -20,5 +21,10 @@ public class SteamPlatform : IPlatform
         }
     }
 
+    public void Update()
+    {
+        SteamAPI.RunCallbacks();
+    }
+
     public IAdditionalContentProvider? AdditionalContentProvider { get; } = new SteamAdditionalContentProvider();
 }

+ 1 - 0
src/PixiEditor.Platform/IPlatform.cs

@@ -6,6 +6,7 @@ public interface IPlatform
     public abstract string Id { get; }
     public abstract string Name { get; }
     public bool PerformHandshake();
+    public void Update();
     public IAdditionalContentProvider? AdditionalContentProvider { get; }
 
     public static void RegisterPlatform(IPlatform platform)

+ 1 - 1
src/PixiEditor.Zoombox/Operations/ZoomDragOperation.cs

@@ -33,7 +33,7 @@ internal class ZoomDragOperation : IDragOperation
         double deltaX = curScreenPos.X - screenScaleOrigin.X;
         double deltaPower = deltaX / 10.0;
 
-        parent.Scale = originalScale * Math.Pow(Zoombox.ScaleFactor, deltaPower);
+        parent.Scale = Math.Clamp(originalScale * Math.Pow(Zoombox.ScaleFactor, deltaPower), parent.MinScale, Zoombox.MaxScale);
 
         VecD shiftedOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
         parent.Center += scaleOrigin - shiftedOrigin;

+ 1 - 1
src/PixiEditor.Zoombox/Zoombox.xaml.cs

@@ -284,7 +284,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
         Angle = 0;
         FlipX = false;
         FlipY = false;
-        Scale = 1 / scaleFactor;
+        Scale = Math.Clamp(1 / scaleFactor, MinScale, MaxScale);
         Center = newSize / 2;
     }
 

+ 38 - 14
src/PixiEditor/App.xaml.cs

@@ -6,6 +6,7 @@ using System.Windows.Interop;
 using System.Windows.Media;
 using System.Windows.Threading;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Helpers;
 using PixiEditor.Models.AppExtensions;
 using PixiEditor.Helpers.UI;
 using PixiEditor.Models.Controllers;
@@ -42,28 +43,37 @@ internal partial class App : Application
 
         if (ParseArgument("--crash (\"?)([A-z0-9:\\/\\ -_.]+)\\1", arguments, out Group[] groups))
         {
-            CrashReport report = CrashReport.Parse(groups[2].Value);
-            MainWindow = new CrashReportDialog(report);
-            MainWindow.Show();
+            try
+            {
+                CrashReport report = CrashReport.Parse(groups[2].Value);
+                MainWindow = new CrashReportDialog(report);
+                MainWindow.Show();
+            }
+            catch (Exception exception)
+            {
+                try
+                {
+                    CrashHelper.SendExceptionInfoToWebhook(exception, true);
+                }
+                finally
+                {
+                    MessageBox.Show("Fatal error", $"Fatal error while trying to open crash report in App.OnStartup()\n{exception}");
+                }
+            }
+
             return;
         }
 
-        #if !STEAM
+#if !STEAM
         if (!HandleNewInstance())
         {
             return;
         }
-        #endif
-
-        AddNativeAssets();
-
-        InitPlatform();
-
-        ExtensionLoader extensionLoader = new ExtensionLoader();
-        extensionLoader.LoadExtensions();
-
+#endif
+        
+        var extensionLoader = InitApp();
+        
         MainWindow = new MainWindow(extensionLoader);
-
         MainWindow.Show();
     }
 
@@ -74,6 +84,20 @@ internal partial class App : Application
         platform.PerformHandshake();
     }
 
+    public ExtensionLoader InitApp()
+    {
+        LoadingWindow.ShowInNewThread();
+
+        AddNativeAssets();
+
+        InitPlatform();
+
+        ExtensionLoader extensionLoader = new ExtensionLoader();
+        extensionLoader.LoadExtensions();
+        
+        return extensionLoader;
+    }
+
     private IPlatform GetActivePlatform()
     {
 #if STEAM

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

@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "وضع النقر بزر الماوس الأيمن",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "أضف اللون الأساسي إلى لوح الألوان",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "أضف اللون الأساسي إلى اللوحة الحالية",
-  "COPY_COLOR": "انسخ اللون"
+  "COPY_COLOR": "انسخ اللون",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "لا يمكن فتح قائمة الملفات",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "لا يمكن استيراد الكل",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "لا يمكن استرداد الملفات بشكل كامل.\nإذا قمت بإرسال التقرير إلى المطورين\nقد يكونوا قادرين على مساعدتك."
 }

+ 5 - 2
src/PixiEditor/Data/Localization/Languages/cs.json

@@ -23,7 +23,7 @@
   "UPDATE_CHANNEL_HELP_TOOLTIP": "Aktualizační  kanály mohou být aktualizovány jen v samostatných verzích aplikace (stáhnutých z https://pixieditor.net/) Steam a Microsoft Store verze zacházejí s jednotlivými verzemi odlišně.",
   "DEBUG": "Debug",
   "ENABLE_DEBUG_MODE": "Aktivovat Debug mód",
-  "OPEN_CRASH_REPORTS_DIR": "Otevřít složku crash reportů",
+  "OPEN_CRASH_REPORTS_DIR": "Otevřít složku zpráv o selhání",
   "DISCORD_RICH_PRESENCE": "Bohatá přítomnost",
   "ENABLED": "Povoleno",
   "SHOW_IMAGE_NAME": "Ukázat jméno obrázku",
@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "Režim pravého tlačítka",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Přidat hlavní barvu do palety",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Přidat hlavní barvu do současné palety",
-  "COPY_COLOR": "Zkopírovat barvu"
+  "COPY_COLOR": "Zkopírovat barvu",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Nepodařilo se otevřít Průzkumníka Souborů",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Nepodařilo se obnovit všechno",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Nepodařilo se plně obnovit všechny soubory.\nKdyž pošlete zprávu o selhání vývojářům, možná vám budou schopni pomoct."
 }

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

@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "Rechtsklick Modus",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Primärfarbe zu Palette hinzufügen",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Primärfarbe der Farbpalette hinzufügen",
-  "COPY_COLOR": "Copy color"
+  "COPY_COLOR": "Farbe kopieren",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Windows Explorer konnte nicht geöffnet werden",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Konnte nicht alle Dokumente wiederherstellen",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Nichte alle Dateien konnten wiederhergestellt werden.\nDie Entwickler können dir eventuell helfen,\nalle Dateien wieder herzustellen."
 }

+ 4 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -589,5 +589,8 @@
   "COPY_COLOR": "Copy color",
 
   "FAILED_DOWNLOADING_TITLE": "Downloading update failed",
-  "FAILED_DOWNLOADING": "Failed downloading the update, you might not have enough space on the disk"
+  "FAILED_DOWNLOADING": "Failed downloading the update, you might not have enough space on the disk",
+  
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Could not recover all",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Could not fully recover all files.\nIf you send the crash report to the developers\nthey might be able to help you."
 }

+ 4 - 1
src/PixiEditor/Data/Localization/Languages/it.json

@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "Modalità tasto destro",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Aggiungi colore primario alla tavolozza",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Aggiungi colore primario alla tavolozza corrente",
-  "COPY_COLOR": "Copia colore"
+  "COPY_COLOR": "Copia colore",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Couldn't open File Explorer",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Could not recover all",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Could not fully recover all files.\nIf you send the crash report to the developers\nthey might be able to help you."
 }

+ 4 - 1
src/PixiEditor/Data/Localization/Languages/pl.json

@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "Akcja prawego przysiku myszy",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Dodaj aktywny kolor do palety",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Dodaj aktywny kolor do aktualnej palety",
-  "COPY_COLOR": "Kopiuj kolor"
+  "COPY_COLOR": "Kopiuj kolor",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Nie udało się otworzyć eksploratora plików",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Nie udało się odzyskać wszystkich plików",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Nie udało się odzyskać wszystkich plików. \nJeżeli wyślesz raport awarii do developerów, być może będą w stanie Ci pomóc."
 }

+ 22 - 19
src/PixiEditor/Data/Localization/Languages/pt-br.json

@@ -561,24 +561,27 @@
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Arraste as alças para transformar a escala. Mantenha pressionada a tecla Shift para dimensionar proporcionalmente. Mantenha pressionada a tecla Alt e arraste uma alça lateral para cortar. Arraste as alças externas para girar.",
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Arraste as alças para transformar a escala. Mantenha pressionada a tecla Shift para dimensionar proporcionalmente. Arraste as alças externas para girar.",
   "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Arraste as alças para transformar a escala. Mantenha pressionada a tecla Shift para dimensionar proporcionalmente.",
-  "OPEN_DOCUMENTATION": "Open documentation",
+  "OPEN_DOCUMENTATION": "Abrir documentação",
   "LOCAL_PALETTE_SOURCE_NAME": "Local",
-  "ERROR_FORBIDDEN_UNIQUE_NAME": "Extension unique name cannot start with 'pixieditor'.",
-  "ERROR_MISSING_METADATA": "Extension metadata key '{0}' is missing.",
-  "ERROR_NO_CLASS_ENTRY": "Extension class entry is missing on path '{0}'.",
-  "ERROR_NO_ENTRY_ASSEMBLY": "Extension entry assembly is missing on path '{0}'.",
-  "ERROR_MISSING_ADDITIONAL_CONTENT": "Your current setup doesn't allow loading this extension. Perhaps you don't own it or don't have it installed. You can purchase it here '{0}'.",
-  "BUY_SUPPORTER_PACK": "Buy Supporter Pack",
-  "NEWS": "News",
-  "DISABLE_NEWS_PANEL": "Disable News panel in startup window",
-  "FAILED_FETCH_NEWS": "Failed to fetch news",
-  "CROP_TO_SELECTION": "Crop to selection",
-  "CROP_TO_SELECTION_DESCRIPTIVE": "Crop image to selection",
-  "SHOW_CONTEXT_MENU": "Show context menu",
-  "ERASE": "Erase",
-  "USE_SECONDARY_COLOR": "Use secondary color",
-  "RIGHT_CLICK_MODE": "Right click mode",
-  "ADD_PRIMARY_COLOR_TO_PALETTE": "Add primary color to palette",
-  "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Add primary color to current palette",
-  "COPY_COLOR": "Copy color"
+  "ERROR_FORBIDDEN_UNIQUE_NAME": "O nome exclusivo da extensão não pode começar com \"pixieditor\".",
+  "ERROR_MISSING_METADATA": "A chave de metadados de extensão '{0}' está ausente.",
+  "ERROR_NO_CLASS_ENTRY": "A entrada da classe de extensão está ausente no caminho '{0}'.",
+  "ERROR_NO_ENTRY_ASSEMBLY": "O conjunto de entrada de extensão está ausente no caminho '{0}'.",
+  "ERROR_MISSING_ADDITIONAL_CONTENT": "Sua configuração atual não permite o carregamento dessa extensão. Talvez você não a possua ou não a tenha instalada. Você pode comprá-la aqui '{0}'",
+  "BUY_SUPPORTER_PACK": "Comprar Pacote de Apoiador",
+  "NEWS": "Notícias",
+  "DISABLE_NEWS_PANEL": "Desativar o painel Notícias na janela de inicialização",
+  "FAILED_FETCH_NEWS": "Falha ao buscar notícias",
+  "CROP_TO_SELECTION": "Cortar para a seleção",
+  "CROP_TO_SELECTION_DESCRIPTIVE": "Cortar imagem para seleção",
+  "SHOW_CONTEXT_MENU": "Mostrar menu de contexto",
+  "ERASE": "Apagar",
+  "USE_SECONDARY_COLOR": "Usar cor secundária",
+  "RIGHT_CLICK_MODE": "Modo de clique direito",
+  "ADD_PRIMARY_COLOR_TO_PALETTE": "Adicionar cor primária à paleta",
+  "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Adicionar cor primária à paleta atual",
+  "COPY_COLOR": "Copiar Cor",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Couldn't open File Explorer",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Could not recover all",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Could not fully recover all files.\nIf you send the crash report to the developers\nthey might be able to help you."
 }

+ 38 - 35
src/PixiEditor/Data/Localization/Languages/ru.json

@@ -12,7 +12,7 @@
   "SHOW_IMAGE_PREVIEW_TASKBAR": "Отображать редактируемую картинку на иконке на панели задач",
   "RECENT_FILE_LENGTH": "Длина списка недавних файлов",
   "RECENT_FILE_LENGTH_TOOLTIP": "Сколько файлов показывать в списке Файл > Недавние. По умолчанию - 8.",
-  "DEFAULT_NEW_SIZE": "Размер нового файла по умолчанию",
+  "DEFAULT_NEW_SIZE": "Размер холста по умолчанию",
   "WIDTH": "Ширина",
   "HEIGHT": "Высота",
   "TOOLS": "Инструменты",
@@ -51,8 +51,8 @@
   "LICENSE": "Лицензия",
   "OPEN_LICENSE": "Открыть лицензию",
   "THIRD_PARTY_LICENSES": "Лицензии третьих сторон",
-  "OPEN_THIRD_PARTY_LICENSES": "Открыть лицензии третьих сторон",
-  "APPLY_TRANSFORM": "Применить трансформирование",
+  "OPEN_THIRD_PARTY_LICENSES": "Открыть сторонние лицензии",
+  "APPLY_TRANSFORM": "Применить масштабирование",
   "INCREASE_TOOL_SIZE": "Увеличить размер кисти",
   "DECREASE_TOOL_SIZE": "Уменьшить размер кисти",
   "TO_INSTALL_UPDATE": "для установки обновления {0}",
@@ -133,9 +133,9 @@
   "MERGE_WITH_BELOW_DESCRIPTIVE": "Объединить выбранный слой со слоем ниже от него",
   "ADD_REFERENCE_LAYER": "Добавить референс",
   "DELETE_REFERENCE_LAYER": "Удалить референс",
-  "TRANSFORM_REFERENCE_LAYER": "Трансформировать референс",
+  "TRANSFORM_REFERENCE_LAYER": "Масштабировать референс",
   "TOGGLE_REFERENCE_LAYER_POS": "Изменить позицию референса",
-  "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Переключение между двумя положениями референса: поверх рисунка и ниже рисунка",
+  "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Переключение между двумя положениями референса: поверх хоста и под холстом",
   "RESET_REFERENCE_LAYER_POS": "Сбросить положение референса",
   "CLIP_CANVAS": "Обрезать прозрачные края",
   "FLIP_IMG_VERTICALLY": "Отразить по вертикали",
@@ -162,7 +162,7 @@
   "PASTE_AS_NEW_LAYER": "Вставить в новый слой",
   "PASTE_AS_NEW_LAYER_DESCRIPTIVE": "Вставить изображение из буфера обмена в новый слой",
   "PASTE_REFERENCE_LAYER": "Вставить референс",
-  "PASTE_REFERENCE_LAYER_DESCRIPTIVE": "Вставить изображение-референс из буфера обмена",
+  "PASTE_REFERENCE_LAYER_DESCRIPTIVE": "Вставить референс из буфера обмена",
   "PASTE_COLOR": "Вставить цвет",
   "PASTE_COLOR_DESCRIPTIVE": "Вставить HEX-код цвета из буфера обмена",
   "PASTE_COLOR_SECONDARY": "Вставить цвет как дополнительный",
@@ -223,7 +223,7 @@
   "CLEAR_SELECTION": "Снять выделение",
   "INVERT_SELECTION": "Инвертировать выделение",
   "INVERT_SELECTION_DESCRIPTIVE": "Инвертировать выделенную область",
-  "TRANSFORM_SELECTED_AREA": "Трансформировать выбранную область",
+  "TRANSFORM_SELECTED_AREA": "Масштабировать выбранную область",
   "NUDGE_SELECTED_LEFT": "Сдвинуть выделение влево",
   "NUDGE_SELECTED_RIGHT": "Сдвинуть выделение вправо",
   "NUDGE_SELECTED_UP": "Сдвинуть выделение вверх",
@@ -275,7 +275,7 @@
   "UNNAMED": "Безымянный",
   "OPEN_COMMAND_DEBUG_WINDOW": "Открыть окно отладки команд",
   "DELETE": "Удалить",
-  "USER_PREFS": "Предпочтения пользователя (Локальные)",
+  "USER_PREFS": "Предпочтения пользователя (Roaming)",
   "SHORTCUT_FILE": "Сочетания клавиш (Локальные)",
   "EDITOR_DATA": "Данные редактора (Локальные)",
   "MOVE_VIEWPORT_TOOLTIP": "Передвигает область просмотра.",
@@ -359,7 +359,7 @@
   "ENABLE_MASK": "Включить маску",
   "SELECTED_AREA_EMPTY": "Выбранная область пуста",
   "NOTHING_TO_COPY": "Нечего копировать",
-  "REFERENCE_LAYER_PATH": "Путь к референс слою",
+  "REFERENCE_LAYER_PATH": "Путь к референсу",
   "FLIP": "Отразить",
   "ROTATION": "Поворот",
   "ROT_IMG_90_D": "Повернуть изображение на 90°",
@@ -481,10 +481,10 @@
   "STOP_IT_TEXT4": "sans undertale reference omg so cool",
   "LINEAR_DODGE_BLEND_MODE": "Линейный осветлитель (добавить)",
   "PRESS_ANY_KEY": "Нажмите любую клавишу",
-  "NONE_SHORTCUT": "Ничего",
+  "NONE_SHORTCUT": "Нет",
   "REFERENCE": "Референс",
-  "PUT_REFERENCE_LAYER_ABOVE": "Показать слой референса",
-  "PUT_REFERENCE_LAYER_BELOW": "Спрятать слой референса",
+  "PUT_REFERENCE_LAYER_ABOVE": "Отображать референс поверх холста",
+  "PUT_REFERENCE_LAYER_BELOW": "Отображать референс под холстом",
   "TOGGLE_VERTICAL_SYMMETRY": "Переключить вертикальную ось симметрии",
   "TOGGLE_HORIZONTAL_SYMMETRY": "Переключить горизонтальную ось симметрии",
   "RESET_VIEWPORT": "Сбросить область просмотра",
@@ -533,52 +533,55 @@
   "IMPORT_PALETTE_FILE": "Импортировать файл палитры",
   "IMPORT_MULTIPLE_PALETTE_COLORS": "Импортировать цвета в палитру",
   "IMPORT_SINGLE_PALETTE_COLOR": "Импортировать цвет в палитру",
-  "IMPORT_AS_REFERENCE_LAYER": "Импортировать как слой референса",
+  "IMPORT_AS_REFERENCE_LAYER": "Импортировать как референс",
   "NAVIGATOR_PICK_ACTION_DISPLAY": "ПКМ чтобы взять цвет, Shift + ПКМ чтобы скопировать цвет в буфер обмена",
   "OPEN_FILE_FROM_CLIPBOARD": "Открыть из буфера обмена",
   "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE": "Открыть из буфера обмена",
   "OPEN_LOCALIZATION_DATA": "Хотите открыть LocalizationData.json? Обновлённая дата добавлена в буфер обмена. Перезапустите чтобы применить изменения.",
-  "DOWNLOADING_LANGUAGE_FAILED": "Не удалось загрузить язык. API ключ может быть использован слишком часто.",
+  "DOWNLOADING_LANGUAGE_FAILED": "Не удалось загрузить язык. \nВозможно был превышена допустимая частота запросов для API ключа.",
   "LOCALIZATION_DATA_NOT_FOUND": "Путь к данным локализации не найден",
   "APPLY": "Применить",
   "UPDATE_SOURCE": "Обновить источник",
   "COPY_TO_CLIPBOARD": "Скопировать в буфер обмена",
-  "LANGUAGE_FILE_NOT_FOUND": "Файл языка не найден. Идёт поиск {0}",
-  "PROJECT_ROOT_NOT_FOUND": "Корневой файл проекта PixiEditor не был найден. Идёт поиск PixiEditor.csproj",
-  "LOCALIZATION_FOLDER_NOT_FOUND": "Путь к папке локализации не найден. Идёт поиск /Data/Localization",
+  "LANGUAGE_FILE_NOT_FOUND": "Файл языка не найден. \nИщем {0}",
+  "PROJECT_ROOT_NOT_FOUND": "Корневой файл проекта PixiEditor не был найден. \nИщем PixiEditor.csproj",
+  "LOCALIZATION_FOLDER_NOT_FOUND": "Путь к папке локализации не найден. \nИщем /Data/Localization",
   "SELECT_A_LANGUAGE": "Выберите язык",
   "DONE": "Готово",
-  "SOURCE_UNSET_OR_MISSING": "Источник отсутствует/не поставлен",
+  "SOURCE_UNSET_OR_MISSING": "Источник отсутствует/не проставлен",
   "SOURCE_NEWER": "Источник новее",
   "SOURCE_UP_TO_DATE": "Источник обновлён",
-  "SOURCE_OLDER": "Облако новее",
-  "COLOR_PICKER_ACTION_DISPLAY_REFERENCE_ONLY": "Нажмите чтобы брать цвета из рефернс слоя.",
+  "SOURCE_OLDER": "Облачная версия новее",
+  "COLOR_PICKER_ACTION_DISPLAY_REFERENCE_ONLY": "Кликните для выбора цвета из референса.",
   "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Нажмите чтобы брать цвета из холста.",
-  "LOCALIZATION_DEBUG_WINDOW_TITLE": "Окно дебага локализаций",
-  "COMMAND_DEBUG_WINDOW_TITLE": "Окно дебага команд",
-  "SHORTCUTS_TITLE": "Ярлыки",
-  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_PERSPECTIVE": "Двигайте ручки чтобы изменить масштаб. Удерживайте Ctrl и передвигайте ручку чтобы передвигать ручку без изменений масштаба. Удерживайте Alt и передвигайте сторону ручки чтобы сдвинуть. Выдвиньте из ручки чтобы повернуть.",
-  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Двигайте ручки чтобы изменить масштаб. Удерживайте Shift чтобы масштабировать пропорционально. Удерживайте Alt и передвигайте сторону ручки чтобы сдвинуть. Выдвиньте из ручки чтобы повернуть.",
-  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Двигайте ручки чтобы изменить масштаб. Удерживайте Shift чтобы масштабировать пропорционально. Выдвиньте из ручек чтобы повернуть.",
-  "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Двигайте ручки чтобы изменить масштаб. Удерживайте Shift чтобы масштабировать пропорционально.",
+  "LOCALIZATION_DEBUG_WINDOW_TITLE": "Окно отладки локализаций",
+  "COMMAND_DEBUG_WINDOW_TITLE": "Окно отладки команд",
+  "SHORTCUTS_TITLE": "Сочетания клавиш",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_PERSPECTIVE": "Перетащите углы и края для масштабирования. Зажмите Ctrl для свободного масштабирования. Удерживайте Alt и перемещайте края для бокового сдвига. Для поворота зажмите и перетащите вблизи выделения.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Перетащите углы и края для масштабирования. Зажмите Shift для пропорционального масштабирования. Удерживайте Alt и перемещайте края для бокового сдвига. Для поворота зажмите и перетащите вблизи выделения.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Двигайте ручки чтобы изменить масштаб. Удерживайте Shift чтобы масштабировать пропорционально. Для поворота зажмите и перетащите вблизи выделения.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Перемещайте углы и края для масштабирования. Зажмите Shift для пропорционального масштабирования.",
   "OPEN_DOCUMENTATION": "Открыть документацию",
   "LOCAL_PALETTE_SOURCE_NAME": "Локально",
-  "ERROR_FORBIDDEN_UNIQUE_NAME": "Уникальное имя приложения не может начинаться с 'pixieditor'.",
-  "ERROR_MISSING_METADATA": "Ключ метадаты приложения '{0}' отсутствует.",
-  "ERROR_NO_CLASS_ENTRY": "Запись в классе приложения отсутствует в папке '{0}'.",
-  "ERROR_NO_ENTRY_ASSEMBLY": "Входная сборка приложения отсутствует в папке '{0}'.",
-  "ERROR_MISSING_ADDITIONAL_CONTENT": "Ваша нынешняя сборка не позволяет загрузить это приложение. Возможно вы не приобрели его или не установили. Вы можете приобрести его здесь '{0}'.",
-  "BUY_SUPPORTER_PACK": "Приобретите Supporter Pack",
+  "ERROR_FORBIDDEN_UNIQUE_NAME": "Уникальное имя расширения не может начинаться с 'pixieditor'.",
+  "ERROR_MISSING_METADATA": "Ключ метадаты расширения '{0}' отсутствует.",
+  "ERROR_NO_CLASS_ENTRY": "Запись в классе расширения отсутствует в папке '{0}'.",
+  "ERROR_NO_ENTRY_ASSEMBLY": "Входная сборка расширения отсутствует в папке '{0}'.",
+  "ERROR_MISSING_ADDITIONAL_CONTENT": "Ваша нынешняя сборка не позволяет загрузить это расширение. Возможно вы не приобрели его или не установили. Вы можете приобрести его здесь '{0}'.",
+  "BUY_SUPPORTER_PACK": "Приобрести Supporter Pack",
   "NEWS": "Новости",
   "DISABLE_NEWS_PANEL": "Отключить новостную панель в окне приветствия",
   "FAILED_FETCH_NEWS": "Не удалось загрузить новости",
   "CROP_TO_SELECTION": "Обрезать выделение",
   "CROP_TO_SELECTION_DESCRIPTIVE": "Обрезать изображение до выделения",
-  "SHOW_CONTEXT_MENU": "Показать меню контекста",
+  "SHOW_CONTEXT_MENU": "Открыть контекстное меню",
   "ERASE": "Стереть",
   "USE_SECONDARY_COLOR": "Использовать дополнительный цвет",
   "RIGHT_CLICK_MODE": "Режим ПКМ",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Добавить основной цвет в палитру",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Добавить основной цвет в нынешнюю палитру",
-  "COPY_COLOR": "Скопировать цвет"
+  "COPY_COLOR": "Скопировать цвет",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Не удалось открыть Проводник",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Не удалось восстановить все файлы",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Не удалось восстановить все файлы.\nЕсли вы отправите отчет о сбое разработчикам, возможно, они смогут вам помочь."
 }

+ 585 - 582
src/PixiEditor/Data/Localization/Languages/tr.json

@@ -1,584 +1,587 @@
 {
-    "RECENT_FILES": "Son Dosyalar",
-    "OPEN_FILE": "Dosya Aç",
-    "NEW_FILE": "Yeni",
-    "RECENT_EMPTY_TEXT": "Çok fazla boşluk",
-    "LANGUAGE": "Dil",
-    "GENERAL": "Genel",
-    "DISCORD": "Discord",
-    "KEY_BINDINGS": "Tuş Atamaları",
-    "MISC": "Çeşitli",
-    "SHOW_STARTUP_WINDOW": "Başlangıç Ekranını Göster",
-    "SHOW_IMAGE_PREVIEW_TASKBAR": "Görev çubuğunda resim önizlemesini göster",
-    "RECENT_FILE_LENGTH": "Son dosya listesi uzunluğu",
-    "RECENT_FILE_LENGTH_TOOLTIP": "Dosya > Son dosyalar altında gösterilen belge sayısı. 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 (https://pixieditor.net'den indirilen) değiştirilebilir.\nSteam ve Microsoft Store sürümleri güncellemeleri ayrı şekilde yönetir.",
-    "DEBUG": "Hata Ayıklama",
-    "ENABLE_DEBUG_MODE": "Hata Ayıklama modunu etkinleştir",
-    "OPEN_CRASH_REPORTS_DIR": "Çökme raporları dizinini aç",
-    "DISCORD_RICH_PRESENCE": "Zengin Durum",
-    "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",
-    "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ğlama Noktası",
-    "RESIZE_IMAGE": "Resmi Yeniden Boyutlandır",
-    "RESIZE": "Yeniden Boyutlandır",
-    "DOCUMENTATION": "Dokümantasyon",
-    "WEBSITE": "Web Sitesi",
-    "OPEN_WEBSITE": "Web sitesini aç",
-    "REPOSITORY": "Depo",
-    "OPEN_REPOSITORY": "Depoyu 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",
-    "TO_INSTALL_UPDATE": "güncellemeyi yüklemek için {0}",
-    "DOWNLOADING_UPDATE": "Güncelleme indiriliyor...",
-    "UPDATE_READY": "Güncelleme yüklemeye hazır. Şimdi yüklemek istiyor musunuz?",
-    "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": "Güncelleme mevcut olup olmadığı kontrol edilemedi.",
-    "VERSION": "Sürüm {0}",
-    "OPEN_TEMP_DIR": "Geçici dizini aç",
-    "OPEN_LOCAL_APPDATA_DIR": "Yerel AppData dizinini aç",
-    "OPEN_ROAMING_APPDATA_DIR": "Gezintili 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 (Gezintili AppData)",
-    "DELETE_SHORTCUT_FILE": "Kısayol dosyasını sil (Gezintili AppData)",
-    "DELETE_EDITOR_DATA": "Düzenleyici verilerini sil (Yerel AppData)",
-    "GENERATE_KEY_BINDINGS_TEMPLATE": "Tuş atama şablonu oluştur",
-    "GENERATE_KEY_BINDINGS_TEMPLATE_DESCRIPTIVE": "Tuş atama 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": "Yeni 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}'yi silmek istediğinizden emin misiniz?\nBu veri tüm yüklemeler için kaybolacak.\n(Tam Yol: {1})",
-    "FAILED_TO_OPEN_FILE": "Dosya açılamadı",
-    "OLD_FILE_FORMAT": "Eski dosya formatı",
-    "OLD_FILE_FORMAT_DESCRIPTION": "Bu .pixi dosyası eski formatta kullanılmıştır,\n artık desteklenmiyor ve açılamaz.",
-    "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": "Hepsini Sıfırla",
-    "LAYER": "Katman",
-    "LAYER_DELETE_SELECTED": "Aktif katmanı/dizini sil",
-    "LAYER_DELETE_SELECTED_DESCRIPTIVE": "Aktif katmanı veya dizini sil",
-    "LAYER_DELETE_ALL_SELECTED": "Tüm seçili katmanları/dizinleri sil",
-    "LAYER_DELETE_ALL_SELECTED_DESCRIPTIVE": "Tüm seçili katmanları ve/veya dizinleri 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 görüntü",
-    "CREATE_NEW_IMAGE": "Yeni görüntü oluştur",
-    "SAVE": "Kaydet",
-    "SAVE_AS": "Farklı Kaydet...",
-    "IMAGE": "Görüntü",
-    "SAVE_IMAGE": "Görüntüyü kaydet",
-    "SAVE_IMAGE_AS": "Yeni olarak görüntüyü kaydet",
-    "DUPLICATE": "Kopyala",
-    "DUPLICATE_SELECTED_LAYER": "Seçili katmanı kopyala",
-    "CREATE_MASK": "Maske oluştur",
-    "DELETE_MASK": "Maskeyi sil",
-    "TOGGLE_MASK": "Maskesi aç/kapat",
-    "APPLY_MASK": "Maskeyi uygula",
-    "TOGGLE_VISIBILITY": "Görünürlüğü aç/kapat",
-    "MOVE_MEMBER_UP": "Üyeyi yukarı taşı",
-    "MOVE_MEMBER_UP_DESCRIPTIVE": "Seçili katmanı veya dizini yukarı taşı",
-    "MOVE_MEMBER_DOWN": "Üyeyi aşağı taşı",
-    "MOVE_MEMBER_DOWN_DESCRIPTIVE": "Seçili katmanı veya dizini aşağı taşı",
-    "MERGE_ALL_SELECTED_LAYERS": "Tüm seçili katmanları birleştir",
-    "MERGE_WITH_ABOVE": "Seçili katmanı üsttekiyle birleştir",
-    "MERGE_WITH_ABOVE_DESCRIPTIVE": "Seçili katmanı üzerindekiyle birleştir",
-    "MERGE_WITH_BELOW": "Seçili katmanı alttakilerle birleştir",
-    "MERGE_WITH_BELOW_DESCRIPTIVE": "Seçili katmanı altındakilerle birleştir",
-    "ADD_REFERENCE_LAYER": "Referans Katman Ekle",
-    "DELETE_REFERENCE_LAYER": "Referans katmanı sil",
-    "TRANSFORM_REFERENCE_LAYER": "Referans katmanını dönüştür",
-    "TOGGLE_REFERENCE_LAYER_POS": "Referans katmanının konumunu değiştir",
-    "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Referans katmanını en üstte veya en altta arasında değiştir",
-    "RESET_REFERENCE_LAYER_POS": "Referans katmanının konumunu sıfırla",
-    "CLIP_CANVAS": "Kanvası Kırp",
-    "FLIP_IMG_VERTICALLY": "Görüntüyü Dikey Olarak Çevir",
-    "FLIP_IMG_HORIZONTALLY": "Görüntüyü Yatay Olarak Çevir",
-    "FLIP_LAYERS_VERTICALLY": "Seçili Katmanları Dikey Olarak Çevir",
-    "FLIP_LAYERS_HORIZONTALLY": "Seçili Katmanları Yatay Olarak Çevir",
-    "ROT_IMG_90": "Görüntüyü 90 derece döndür",
-    "ROT_IMG_180": "Görüntüyü 180 derece döndür",
-    "ROT_IMG_-90": "Görüntüyü -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_PIXELS": "Pikselleri sil",
-    "DELETE_PIXELS_DESCRIPTIVE": "Seçili pikselleri sil",
-    "RESIZE_DOCUMENT": "Belgeyi Yeniden Boyutlandır",
-    "RESIZE_CANVAS": "Kanvası 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 bir katman olarak yapıştır",
-    "PASTE_REFERENCE_LAYER": "Referans katmanını yapıştır",
-    "PASTE_REFERENCE_LAYER_DESCRIPTIVE": "Panodaki içeriği referans katmanı olarak yapıştır",
-    "PASTE_COLOR": "Rengi yapıştır",
-    "PASTE_COLOR_DESCRIPTIVE": "Panodan rengi yapıştır",
-    "PASTE_COLOR_SECONDARY": "İkincil 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 renk ile değiştir",
-    "REPLACE_SECONDARY_BY_PRIMARY_DESCRIPTIVE": "İkincil rengi birincil renk ile değiştir",
-    "REPLACE_PRIMARY_BY_SECONDARY": "Birincil rengi ikincil renk 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": "Geçerli palet seçilenle değiştirilsin mi?",
-    "REPLACE_PALETTE": "Geçerli 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": "Paletin ilk rengini seçin",
-    "SELECT_COLOR_2_DESCRIPTIVE": "Paletin ikinci rengini seçin",
-    "SELECT_COLOR_3_DESCRIPTIVE": "Paletin üçüncü rengini seçin",
-    "SELECT_COLOR_4_DESCRIPTIVE": "Paletin dördüncü rengini seçin",
-    "SELECT_COLOR_5_DESCRIPTIVE": "Paletin beşinci rengini seçin",
-    "SELECT_COLOR_6_DESCRIPTIVE": "Paletin altıncı rengini seçin",
-    "SELECT_COLOR_7_DESCRIPTIVE": "Paletin yedinci rengini seçin",
-    "SELECT_COLOR_8_DESCRIPTIVE": "Paletin sekizinci rengini seçin",
-    "SELECT_COLOR_9_DESCRIPTIVE": "Paletin dokuzuncu rengini seçin",
-    "SELECT_COLOR_10_DESCRIPTIVE": "Paletin onuncu rengini seçin",
-    "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": "Tersine Çevir",
-    "SELECTION": "Seçim",
-    "SELECT_ALL": "Hepsini Seç",
-    "SELECT_ALL_DESCRIPTIVE": "Hepsini seç",
-    "CLEAR_SELECTION": "Seçimi Temizle",
-    "INVERT_SELECTION": "Seçimi Tersine Çevir",
-    "INVERT_SELECTION_DESCRIPTIVE": "Seçimi tersine ç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": "Yeni maske oluştur",
-    "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 maskeye çıkar",
-    "INTERSECT_SELECTION_MASK": "Seçimi maskeyle kesiştir",
-    "SELECTION_TO_MASK": "Seçimi maskeye dönüştür",
-    "TO_NEW_MASK": "yeni maske için",
-    "ADD_TO_MASK": "maskeye ekle",
-    "SUBTRACT_FROM_MASK": "maskeden çıkar",
-    "INTERSECT_WITH_MASK": "maskelerle kesiştir",
-    "STYLUS": "Stylus",
-    "TOGGLE_PEN_MODE": "Kalem modunu aç/kapat",
-    "UNDO": "Geri Al",
-    "UNDO_DESCRIPTIVE": "Son işlemi geri al",
-    "REDO": "Yinele",
-    "REDO_DESCRIPTIVE": "Son işlemi yeniden yap",
-    "WINDOWS": "Windows",
-    "TOGGLE_GRIDLINES": "Kılavuz çizgilerini aç/kapat",
-    "ZOOM_IN": "Yakınlaştır",
-    "ZOOM_OUT": "Uzaklaştır",
-    "NEW_WINDOW_FOR_IMG": "Mevcut resim için yeni pencere aç",
-    "CENTER_ACTIVE_VIEWPORT": "Etkin görünümü ortala",
-    "FLIP_VIEWPORT_HORIZONTALLY": "Görünümü yatay olarak çevir",
-    "FLIP_VIEWPORT_VERTICALLY": "Görünümü dikey olarak çevir",
-    "SETTINGS": "Ayarlar",
-    "OPEN_SETTINGS": "Ayarları aç",
-    "OPEN_SETTINGS_DESCRIPTIVE": "Ayarlar penceresini aç",
-    "OPEN_STARTUP_WINDOW": "Başlangıç penceresini aç",
-    "OPEN_SHORTCUT_WINDOW": "Kısayol penceresini aç",
-    "OPEN_ABOUT_WINDOW": "Hakkında penceresini aç",
-    "OPEN_NAVIGATION_WINDOW": "Gezinme penceresini aç",
-    "ERROR": "Hata",
-    "INTERNAL_ERROR": "Dahili hata",
-    "ERROR_SAVE_LOCATION": "Dosyayı belirtilen konuma kaydedemedim",
-    "ERROR_WHILE_SAVING": "Kaydetme sırasında dahili bir hata oluştu. Lütfen tekrar deneyin.",
-    "UNKNOWN_ERROR_SAVING": "Kaydetme sırasında 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": "İsimsiz",
-    "OPEN_COMMAND_DEBUG_WINDOW": "Komut hata ayıklama penceresini aç",
-    "DELETE": "Sil",
-    "USER_PREFS": "Kullanıcı tercihleri (Gezinti)",
-    "SHORTCUT_FILE": "Kısayol dosyası (Gezinti)",
-    "EDITOR_DATA": "Düzenleyici verileri (Yerel)",
-    "MOVE_VIEWPORT_TOOLTIP": "Görünümü taşı. ({0})",
-    "MOVE_VIEWPORT_ACTION_DISPLAY": "Görünümü kaydırmak için tıklayın ve sürükleyin",
-    "MOVE_TOOL_TOOLTIP": "Seçili pikselleri taşır ({0}). Tüm katmanları taşımak için Ctrl tuşuna basılı tutun.",
-    "MOVE_TOOL_ACTION_DISPLAY": "Seçili pikselleri taşımak için fareyi basılı tutun. Tüm katmanları taşımak için Ctrl tuşuna basılı tutun.",
-    "PEN_TOOL_TOOLTIP": "Kalem. ({0})",
-    "PEN_TOOL_ACTION_DISPLAY": "Çizmek için tıklayın ve sürükleyin.",
-    "PIXEL_PERFECT_SETTING": "Mükemmel piksel",
-    "RECTANGLE_TOOL_TOOLTIP": "Tuval üzerinde dikdörtgen çizer ({0}). Kare çizmek için Shift tuşunu basılı tutun.",
-    "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT": "Dikdörtgen çizmek için tıklayın ve sürükleyin. Kare çizmek için Shift tuşunu basılı tutun.",
-    "RECTANGLE_TOOL_ACTION_DISPLAY_SHIFT": "Kare çizmek için tıklayın ve sürükleyin.",
-    "KEEP_ORIGINAL_IMAGE_SETTING": "Orijinal resmi koru",
-    "ROTATE_VIEWPORT_TOOLTIP": "Görünümü döndür. ({0})",
-    "ROTATE_VIEWPORT_ACTION_DISPLAY": "Görünümü döndürmek için tıklayın ve sürükleyin",
-    "SELECT_TOOL_TOOLTIP": "Alan seçer. ({0})",
-    "SELECT_TOOL_ACTION_DISPLAY_DEFAULT": "Bir alan seçmek için tıklayın ve sürükleyin. Mevcut seçime eklemek için Shift tuşunu basılı tutun. Çıkarmak için Ctrl tuşuna basılı tutun.",
-    "SELECT_TOOL_ACTION_DISPLAY_SHIFT": "Mevcut seçime eklemek için tıklayın ve sürükleyin.",
-    "SELECT_TOOL_ACTION_DISPLAY_CTRL": "Mevcut seçimden çıkarmak için tıklayın ve sürükleyin.",
-    "ZOOM_TOOL_TOOLTIP": "Görünümü yakınlaştırır ({0}). Yakınlaştırmak için tıklayın, alt tuşunu basılı tutun ve tıklayınarak uzaklaştırın.",
-    "ZOOM_TOOL_ACTION_DISPLAY_DEFAULT": "Yakınlaştırmak için tıklayın ve sürükleyin. Yakınlaştırmak için Ctrl tuşunu basılı tutun ve tıklayınarak uzaklaştırın.",
-    "ZOOM_TOOL_ACTION_DISPLAY_CTRL": "Yakınlaştırmak için tıklayın ve sürükleyin. Uzaklaştırmak için Ctrl tuşunu bırakın ve tıklayın.",
-    "BRIGHTNESS_TOOL_TOOLTIP": "Pikselleri daha parlak veya daha koyu hale getirir ({0}). Pikselleri daha koyu yapmak için Ctrl tuşunu basılı tutun.",
-    "BRIGHTNESS_TOOL_ACTION_DISPLAY_DEFAULT": "Pikselleri daha parlak hale getirmek için üzerlerine çizin. Pikselleri daha koyu yapmak için Ctrl tuşunu basılı tutun.",
-    "BRIGHTNESS_TOOL_ACTION_DISPLAY_CTRL": "Pikselleri daha koyu yapmak için üzerlerine çizin. Daha parlak hale getirmek 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ı gizlemek için Shift tuşunu basılı tutun",
-    "ELLIPSE_TOOL_TOOLTIP": "Tuval üzerinde 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 tıklayın ve sürükleyin. Bir daire çizmek için Shift tuşunu basılı tutun.",
-    "ELLIPSE_TOOL_ACTION_DISPLAY_SHIFT": "Bir daire çizmek için tıklayın ve sürükleyin.",
-    "ERASER_TOOL_TOOLTIP": "Piksellerden rengi siler. ({0})",
-    "ERASER_TOOL_ACTION_DISPLAY": "Silmek için tıklayın ve sürükleyin.",
-    "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. Yalnızca geçerli katmanları dikkate almak için Ctrl tuşunu bırakın.",
-    "LASSO_TOOL_TOOLTIP": "Lasso. ({0})",
-    "LASSO_TOOL_ACTION_DISPLAY_DEFAULT": "Lasso içindeki pikselleri seçmek için tıklayın ve sürükleyin. Mevcut seçime eklemek için Shift tuşunu basılı tutun. Çıkarmak için Ctrl tuşunu basılı tutun.",
-    "LASSO_TOOL_ACTION_DISPLAY_SHIFT": "Lasso içindeki pikselleri seçmek için tıklayın ve sürükleyin.",
-    "LASSO_TOOL_ACTION_DISPLAY_CTRL": "Lasso içindeki pikselleri mevcut seçimden çıkarmak için tıklayın ve sürükleyin.",
-    "LINE_TOOL_TOOLTIP": "Tuval üzerinde çizgi çizer ({0}). Yakalama etkinleştirmek için Shift tuşunu basılı tutun.",
-    "LINE_TOOL_ACTION_DISPLAY_DEFAULT": "Bir çizgi çizmek için tıklayın ve sürükleyin. Yakalamayı etkinleştirmek için Shift tuşunu basılı tutun.",
-    "LINE_TOOL_ACTION_DISPLAY_SHIFT": "Yakalama etkinleştirilmiş bir çizgi çizmek için tıklayın ve sürükleyin.",
-    "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": "Doldurma",
-    "LASSO_TOOL": "Lasso",
-    "LINE_TOOL": "Çizgi",
-    "MAGIC_WAND_TOOL": "Sihirli Değnek",
-    "MOVE_TOOL": "Taşı",
-    "MOVE_VIEWPORT_TOOL": "Görünümü Taşı",
-    "RECTANGLE_TOOL": "Dikdörtgen",
-    "ROTATE_VIEWPORT_TOOL": "Görünümü Döndür",
-    "SELECT_TOOL_NAME": "Seçim",
-    "ZOOM_TOOL": "Yakınlaştırma",
-    "SHAPE_LABEL": "Şekil",
-    "MODE_LABEL": "Mod",
-    "SCOPE_LABEL": "Kapsam",
-    "FILL_SHAPE_LABEL": "Şekli Doldur",
-    "FILL_COLOR_LABEL": "Renk Doldur",
-    "TOOL_SIZE_LABEL": "Araç Boyutu",
-    "STRENGTH_LABEL": "Güç",
-    "NEW": "Yeni",
-    "ADD": "Ekle",
-    "SUBTRACT": "Çıkar",
-    "INTERSECT": "Kesişim",
-    "RECTANGLE": "Dikdörtgen",
-    "CIRCLE": "Daire",
-    "ABOUT": "Hakkında",
-    "MINIMIZE": "Simge Durumuna Küçült",
-    "RESTORE": "Geri Al",
-    "MAXIMIZE": "Büyüt",
-    "CLOSE": "Kapat",
-    "EXPORT_SIZE_HINT": "Görüntüyü paylaşmak isterseniz, en iyi netlik için {0}% önerilir",
-    "CREATE": "Oluştur",
-    "BASE_LAYER_NAME": "Ana katman",
-    "ENABLE_MASK": "Maskeleri Etkinleştir",
-    "SELECTED_AREA_EMPTY": "Seçilen 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": "Görüntüyü 90° Döndür",
-    "ROT_IMG_180_D": "Görüntüyü 180° Döndür",
-    "ROT_IMG_-90_D": "Görüntüyü -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 rengi 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",
-    "NAVIGATION_TITLE": "Gezinme",
-    "NORMAL_BLEND_MODE": "Normal",
-    "DARKEN_BLEND_MODE": "Karartma",
-    "MULTIPLY_BLEND_MODE": "Çarpma",
-    "COLOR_BURN_BLEND_MODE": "Renk Yakma",
-    "LIGHTEN_BLEND_MODE": "Aydınlatma",
-    "SCREEN_BLEND_MODE": "Ekran",
-    "COLOR_DODGE_BLEND_MODE": "Renk Geçişi",
-    "OVERLAY_BLEND_MODE": "Üzerine Yerleştirme",
-    "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": "Desteklenmeyen",
-    "RESTART": "Yeniden Başlat",
-    "SORT_BY": "Sırala",
-    "NAME": "İsim",
-    "COLORS": "Renkler",
-    "DEFAULT": "Varsayılan",
-    "ALPHABETICAL": "Alfabetik",
-    "COLOR_COUNT": "Renk sayısı",
-    "ANY": "Herhangi biri",
-    "MAX": "Maksimum",
-    "MIN": "Minimum",
-    "EXACT": "Tam",
-    "ASCENDING": "Artan",
-    "DESCENDING": "Azalan",
-    "NAME_IS_TOO_LONG": "İsim çok uzun",
-    "STOP_IT_TEXT1": "Yeter. Dosya isimlerinizi düzgünleştirin.",
-    "STOP_IT_TEXT2": "Bu isimleri kopyalamayı bırakabilir misiniz?",
-    "REPLACER_TOOLTIP": "Palet renginin üzerine sağ tıklayın ve 'Değiştir' seçeneğini seçin veya buraya bırakın.",
-    "CLICK_TO_CHOOSE_COLOR": "Renk 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. Başka bir palet renginin üzerine sürükleyip bırakarak değiştirin.",
-    "ADD_FROM_SWATCHES": "Renk örneklerinden ekle",
-    "ADD_COLOR_TO_PALETTE": "Palet'e renk ekle",
-    "USE_IN_CURRENT_IMAGE": "Geçerli görüntüde kullan",
-    "ADD_TO_FAVORITES": "Favorilere ekle",
-    "BROWSE_PALETTES": "Paletlere göz at",
-    "LOAD_PALETTE": "Paleti 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",
-    "MIDDLE_RIGHT": "Sağ orta",
-    "BOTTOM_LEFT": "Sol alt",
-    "BOTTOM_CENTER": "Orta alt",
-    "BOTTOM_RIGHT": "Sağ alt",
-    "CLIP_TO_BELOW": "Alt katmana 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": "Palet alınamadı",
-    "NO_PALETTES_FOUND": "Palet bulunamadı.",
-    "LOSPEC_LINK_TEXT": "İşte bazılarına buradan ulaşabilirsiniz: 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} kaynaklı kısayollar başarıyla içe aktarıldı.",
-    "SHORTCUT_PROVIDER_DETECTED": "{0} yüklendiği tespit edildi. Kısayollarını içe aktarmak istiyor musunuz?",
-    "IMPORT_FROM_INSTALLATION": "Yüklemeyi içe aktar",
-    "IMPORT_INSTALLATION_OPTION1": "Yüklemeyi 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ı bozuldu, varsayılanlara sıfırlanıyor.",
-    "FAILED_DOWNLOAD_PALETTE": "Palet indirilemedi",
-    "FILE_INCORRECT_FORMAT": "Dosya doğru bir formatta değil",
-    "INVALID_FILE": "Geçersiz dosya",
-    "SHORTCUTS_FILE_INCORRECT_FORMAT": "Kısayollar dosyası doğru bir formatta değil",
-    "UNSUPPORTED_FILE_FORMAT": "Bu dosya formatı desteklenmiyor",
-    "ALREADY_ASSIGNED": "Zaten atanmış",
-    "REPLACE": "Değiştir",
-    "SWAP": "Değiştir",
-    "SHORTCUT_ALREADY_ASSIGNED_SWAP": "Bu kısayol zaten '{0}' için atanmış\nMevcut kısayolu değiştirmek veya ikisini değiştirmek istiyor musunuz?",
-    "SHORTCUT_ALREADY_ASSIGNED_OVERWRITE": "Bu kısayol zaten '{0}' için atanmış\nMevcut kısayolu değiştirmek istiyor musunuz?",
-    "UNSAVED_CHANGES": "Kaydedilmemiş değişiklikler",
-    "DOCUMENT_MODIFIED_SAVE": "Belge değiştirildi. Değişiklikleri kaydetmek istiyor musunuz?",
-    "SESSION_UNSAVED_DATA": "{0} kaydedilmemiş veri içeriyor. Emin misiniz?",
-    "PROJECT_MAINTAINERS": "Proje Bakımcıları",
-    "OTHER_AWESOME_CONTRIBUTORS": "Ve diğer harika katkıda bulunanlar",
-    "HELP": "Yardım",
-    "STOP_IT_TEXT3": "Hayır gerçekten, yeter.",
-    "STOP_IT_TEXT4": "Daha iyi yapacak başka bir şeyiniz yok mu?",
-    "LINEAR_DODGE_BLEND_MODE": "Lineer dodge (Ekleme)",
-    "PRESS_ANY_KEY": "Herhangi bir tuşa basın",
-    "NONE_SHORTCUT": "Hiçbiri",
-    "REFERENCE": "Referans",
-    "PUT_REFERENCE_LAYER_ABOVE": "Referans katmanını üste yerleştir",
-    "PUT_REFERENCE_LAYER_BELOW": "Referans katmanını alta yerleştir",
-    "TOGGLE_VERTICAL_SYMMETRY": "Dikey simetriyi aç/kapat",
-    "TOGGLE_HORIZONTAL_SYMMETRY": "Yatay simetriyi aç/kapat",
-    "RESET_VIEWPORT": "Görünümü sıfırla",
-    "VIEWPORT_SETTINGS": "Görünüm ayarları",
-    "MOVE_TOOL_ACTION_DISPLAY_TRANSFORMING": "Tıklanıp basılı tutarak seçili katmanlardaki pikselleri taşımak için fareyi kullanın.",
-    "MOVE_TOOL_ACTION_DISPLAY_CTRL": "Tüm katmanları taşımak için fareyi 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": "Hata: {0}",
-    "DROP_PALETTE": "Paleti Buraya Bırak",
-    "SECURITY_ERROR": "Güvenlik Hatası",
-    "SECURITY_ERROR_MSG": "Belirtilen konuma yazma izni yok.",
-    "IO_ERROR": "G/Ç Hatası",
-    "IO_ERROR_MSG": "Disk'e 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 bulunmuyor.",
-    "ALL_LAYERS": "Tüm Katmanlar",
-    "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": "Dosya türü '{0}' 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 palet içine aktar",
-    "IMPORT_SINGLE_PALETTE_COLOR": "Renkleri palet içine aktar",
-    "IMPORT_AS_REFERENCE_LAYER": "Referans katmanı olarak içe aktar",
-    "NAVIGATOR_PICK_ACTION_DISPLAY": "Renk seçmek için sağ tıklayın, Renkleri panoya kopyalamak için Shift tuşuna basılı tutun",
-    "OPEN_FILE_FROM_CLIPBOARD": "Panodan aç",
-    "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE": "Panodan aç",
-    "OPEN_LOCALIZATION_DATA": "LocalizationData.json dosyasını mı açmak istiyorsunuz?\nGüncellenmiş tarih panoya kopyalandı.\nNot Değişiklikler, yeniden başlatma yapılana kadar uygulanmayacaktır",
-    "DOWNLOADING_LANGUAGE_FAILED": "Dil indirme başarısız oldu.\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ı.\n'{0}' dosyası aranıyor",
-    "PROJECT_ROOT_NOT_FOUND": "PixiEditor proje kökü bulunamadı.\nPixiEditor.csproj dosyası aranıyor",
-    "LOCALIZATION_FOLDER_NOT_FOUND": "Yerelleştirme klasörü bulunamadı.\n/Data/Localization dizini aranıyor",
-    "SELECT_A_LANGUAGE": "Bir dil seçin",
-    "DONE": "Tamamlandı",
-    "SOURCE_UNSET_OR_MISSING": "Kaynak eksik/belirtilmemiş",
-    "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 renk seçmek için tıklayın.",
-    "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Tuvalden renk 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üştürmeyi ölçeklendirmek için kolları sürükleyin. Bir kolun üzerine tıklayıp Ctrl tuşunu basılı tutarak kolun serbestçe hareket etmesini sağlayın. Orantılı olarak ölçeklendirmek için Shift tuşunu basılı tutun. Bir yan kolun üzerine tıklayıp Alt tuşunu basılı tutarak kaydırın. Dış kolları sürükleyerek döndürün.",
-    "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Dönüştürmeyi ölçeklendirmek için kolları sürükleyin. Orantılı olarak ölçeklendirmek için Shift tuşunu basılı tutun. Bir yan kolun üzerine tıklayıp Alt tuşunu basılı tutarak kaydırın. Dış kolları sürükleyerek döndürün.",
-    "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Dönüştürmeyi ölçeklendirmek için kolları sürükleyin. Shift tuşunu basılı tutunarak orantılı olarak ölçeklendirin. Dış kolları sürükleyerek döndürün.",
-    "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Dönüştürmeyi ölçeklendirmek için kolları sürükleyin. Shift tuşunu basılı tutarak orantılı olarak ölçeklendirin.",
-    "OPEN_DOCUMENTATION": "Dokümantasyonu aç",
-    "LOCAL_PALETTE_SOURCE_NAME": "Yerel",
-    "ERROR_FORBIDDEN_UNIQUE_NAME": "Uzantı benzersiz adı 'pixieditor' ile başlayamaz.",
-    "ERROR_MISSING_METADATA": "Uzantı metaverisi '{0}' eksik.",
-    "ERROR_NO_CLASS_ENTRY": "Uzantı sınıf girişi '{0}' yolda eksik.",
-    "ERROR_NO_ENTRY_ASSEMBLY": "Uzantı giriş derlemesi '{0}' yolda eksik.",
-    "ERROR_MISSING_ADDITIONAL_CONTENT": "Mevcut yapılandırmanız bu uzantının yüklenmesine izin vermiyor. Belki de sahibi değilsiniz veya yüklü değil. Uzantıyı buradan satın alabilirsiniz '{0}'.",
-    "BUY_SUPPORTER_PACK": "Destekçi Paketi Satın Al",
-    "NEWS": "Haberler",
-    "DISABLE_NEWS_PANEL": "Başlangıç penceresinde Haberler panelini devre dışı bırak",
-    "FAILED_FETCH_NEWS": "Haberleri alırken hata oluştu",
-    "CROP_TO_SELECTION": "Seçime Kırp",
-    "CROP_TO_SELECTION_DESCRIPTIVE": "Resmi seçime 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",
-    "COPY_COLOR": "Rengi Kopyala"
+  "RECENT_FILES": "Son Dosyalar",
+  "OPEN_FILE": "Dosya Aç",
+  "NEW_FILE": "Yeni",
+  "RECENT_EMPTY_TEXT": "Çok fazla boşluk",
+  "LANGUAGE": "Dil",
+  "GENERAL": "Genel",
+  "DISCORD": "Discord",
+  "KEY_BINDINGS": "Tuş Atamaları",
+  "MISC": "Çeşitli",
+  "SHOW_STARTUP_WINDOW": "Başlangıç Ekranını Göster",
+  "SHOW_IMAGE_PREVIEW_TASKBAR": "Görev çubuğunda resim önizlemesini göster",
+  "RECENT_FILE_LENGTH": "Son dosya listesi uzunluğu",
+  "RECENT_FILE_LENGTH_TOOLTIP": "Dosya > Son dosyalar altında gösterilen belge sayısı. 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 (https://pixieditor.net'den indirilen) değiştirilebilir.\nSteam ve Microsoft Store sürümleri güncellemeleri ayrı şekilde yönetir.",
+  "DEBUG": "Hata Ayıklama",
+  "ENABLE_DEBUG_MODE": "Hata Ayıklama modunu etkinleştir",
+  "OPEN_CRASH_REPORTS_DIR": "Çökme raporları dizinini aç",
+  "DISCORD_RICH_PRESENCE": "Zengin Durum",
+  "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",
+  "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ğlama Noktası",
+  "RESIZE_IMAGE": "Resmi Yeniden Boyutlandır",
+  "RESIZE": "Yeniden Boyutlandır",
+  "DOCUMENTATION": "Dokümantasyon",
+  "WEBSITE": "Web Sitesi",
+  "OPEN_WEBSITE": "Web sitesini aç",
+  "REPOSITORY": "Depo",
+  "OPEN_REPOSITORY": "Depoyu 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",
+  "TO_INSTALL_UPDATE": "güncellemeyi yüklemek için {0}",
+  "DOWNLOADING_UPDATE": "Güncelleme indiriliyor...",
+  "UPDATE_READY": "Güncelleme yüklemeye hazır. Şimdi yüklemek istiyor musunuz?",
+  "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": "Güncelleme mevcut olup olmadığı kontrol edilemedi.",
+  "VERSION": "Sürüm {0}",
+  "OPEN_TEMP_DIR": "Geçici dizini aç",
+  "OPEN_LOCAL_APPDATA_DIR": "Yerel AppData dizinini aç",
+  "OPEN_ROAMING_APPDATA_DIR": "Gezintili 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 (Gezintili AppData)",
+  "DELETE_SHORTCUT_FILE": "Kısayol dosyasını sil (Gezintili AppData)",
+  "DELETE_EDITOR_DATA": "Düzenleyici verilerini sil (Yerel AppData)",
+  "GENERATE_KEY_BINDINGS_TEMPLATE": "Tuş atama şablonu oluştur",
+  "GENERATE_KEY_BINDINGS_TEMPLATE_DESCRIPTIVE": "Tuş atama 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": "Yeni 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}'yi silmek istediğinizden emin misiniz?\nBu veri tüm yüklemeler için kaybolacak.\n(Tam Yol: {1})",
+  "FAILED_TO_OPEN_FILE": "Dosya açılamadı",
+  "OLD_FILE_FORMAT": "Eski dosya formatı",
+  "OLD_FILE_FORMAT_DESCRIPTION": "Bu .pixi dosyası eski formatta kullanılmıştır,\n artık desteklenmiyor ve açılamaz.",
+  "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": "Hepsini Sıfırla",
+  "LAYER": "Katman",
+  "LAYER_DELETE_SELECTED": "Aktif katmanı/dizini sil",
+  "LAYER_DELETE_SELECTED_DESCRIPTIVE": "Aktif katmanı veya dizini sil",
+  "LAYER_DELETE_ALL_SELECTED": "Tüm seçili katmanları/dizinleri sil",
+  "LAYER_DELETE_ALL_SELECTED_DESCRIPTIVE": "Tüm seçili katmanları ve/veya dizinleri 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 görüntü",
+  "CREATE_NEW_IMAGE": "Yeni görüntü oluştur",
+  "SAVE": "Kaydet",
+  "SAVE_AS": "Farklı Kaydet...",
+  "IMAGE": "Görüntü",
+  "SAVE_IMAGE": "Görüntüyü kaydet",
+  "SAVE_IMAGE_AS": "Yeni olarak görüntüyü kaydet",
+  "DUPLICATE": "Kopyala",
+  "DUPLICATE_SELECTED_LAYER": "Seçili katmanı kopyala",
+  "CREATE_MASK": "Maske oluştur",
+  "DELETE_MASK": "Maskeyi sil",
+  "TOGGLE_MASK": "Maskesi aç/kapat",
+  "APPLY_MASK": "Maskeyi uygula",
+  "TOGGLE_VISIBILITY": "Görünürlüğü aç/kapat",
+  "MOVE_MEMBER_UP": "Üyeyi yukarı taşı",
+  "MOVE_MEMBER_UP_DESCRIPTIVE": "Seçili katmanı veya dizini yukarı taşı",
+  "MOVE_MEMBER_DOWN": "Üyeyi aşağı taşı",
+  "MOVE_MEMBER_DOWN_DESCRIPTIVE": "Seçili katmanı veya dizini aşağı taşı",
+  "MERGE_ALL_SELECTED_LAYERS": "Tüm seçili katmanları birleştir",
+  "MERGE_WITH_ABOVE": "Seçili katmanı üsttekiyle birleştir",
+  "MERGE_WITH_ABOVE_DESCRIPTIVE": "Seçili katmanı üzerindekiyle birleştir",
+  "MERGE_WITH_BELOW": "Seçili katmanı alttakilerle birleştir",
+  "MERGE_WITH_BELOW_DESCRIPTIVE": "Seçili katmanı altındakilerle birleştir",
+  "ADD_REFERENCE_LAYER": "Referans Katman Ekle",
+  "DELETE_REFERENCE_LAYER": "Referans katmanı sil",
+  "TRANSFORM_REFERENCE_LAYER": "Referans katmanını dönüştür",
+  "TOGGLE_REFERENCE_LAYER_POS": "Referans katmanının konumunu değiştir",
+  "TOGGLE_REFERENCE_LAYER_POS_DESCRIPTIVE": "Referans katmanını en üstte veya en altta arasında değiştir",
+  "RESET_REFERENCE_LAYER_POS": "Referans katmanının konumunu sıfırla",
+  "CLIP_CANVAS": "Kanvası Kırp",
+  "FLIP_IMG_VERTICALLY": "Görüntüyü Dikey Olarak Çevir",
+  "FLIP_IMG_HORIZONTALLY": "Görüntüyü Yatay Olarak Çevir",
+  "FLIP_LAYERS_VERTICALLY": "Seçili Katmanları Dikey Olarak Çevir",
+  "FLIP_LAYERS_HORIZONTALLY": "Seçili Katmanları Yatay Olarak Çevir",
+  "ROT_IMG_90": "Görüntüyü 90 derece döndür",
+  "ROT_IMG_180": "Görüntüyü 180 derece döndür",
+  "ROT_IMG_-90": "Görüntüyü -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_PIXELS": "Pikselleri sil",
+  "DELETE_PIXELS_DESCRIPTIVE": "Seçili pikselleri sil",
+  "RESIZE_DOCUMENT": "Belgeyi Yeniden Boyutlandır",
+  "RESIZE_CANVAS": "Kanvası 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 bir katman olarak yapıştır",
+  "PASTE_REFERENCE_LAYER": "Referans katmanını yapıştır",
+  "PASTE_REFERENCE_LAYER_DESCRIPTIVE": "Panodaki içeriği referans katmanı olarak yapıştır",
+  "PASTE_COLOR": "Rengi yapıştır",
+  "PASTE_COLOR_DESCRIPTIVE": "Panodan rengi yapıştır",
+  "PASTE_COLOR_SECONDARY": "İkincil 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 renk ile değiştir",
+  "REPLACE_SECONDARY_BY_PRIMARY_DESCRIPTIVE": "İkincil rengi birincil renk ile değiştir",
+  "REPLACE_PRIMARY_BY_SECONDARY": "Birincil rengi ikincil renk 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": "Geçerli palet seçilenle değiştirilsin mi?",
+  "REPLACE_PALETTE": "Geçerli 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": "Paletin ilk rengini seçin",
+  "SELECT_COLOR_2_DESCRIPTIVE": "Paletin ikinci rengini seçin",
+  "SELECT_COLOR_3_DESCRIPTIVE": "Paletin üçüncü rengini seçin",
+  "SELECT_COLOR_4_DESCRIPTIVE": "Paletin dördüncü rengini seçin",
+  "SELECT_COLOR_5_DESCRIPTIVE": "Paletin beşinci rengini seçin",
+  "SELECT_COLOR_6_DESCRIPTIVE": "Paletin altıncı rengini seçin",
+  "SELECT_COLOR_7_DESCRIPTIVE": "Paletin yedinci rengini seçin",
+  "SELECT_COLOR_8_DESCRIPTIVE": "Paletin sekizinci rengini seçin",
+  "SELECT_COLOR_9_DESCRIPTIVE": "Paletin dokuzuncu rengini seçin",
+  "SELECT_COLOR_10_DESCRIPTIVE": "Paletin onuncu rengini seçin",
+  "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": "Tersine Çevir",
+  "SELECTION": "Seçim",
+  "SELECT_ALL": "Hepsini Seç",
+  "SELECT_ALL_DESCRIPTIVE": "Hepsini seç",
+  "CLEAR_SELECTION": "Seçimi Temizle",
+  "INVERT_SELECTION": "Seçimi Tersine Çevir",
+  "INVERT_SELECTION_DESCRIPTIVE": "Seçimi tersine ç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": "Yeni maske oluştur",
+  "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 maskeye çıkar",
+  "INTERSECT_SELECTION_MASK": "Seçimi maskeyle kesiştir",
+  "SELECTION_TO_MASK": "Seçimi maskeye dönüştür",
+  "TO_NEW_MASK": "yeni maske için",
+  "ADD_TO_MASK": "maskeye ekle",
+  "SUBTRACT_FROM_MASK": "maskeden çıkar",
+  "INTERSECT_WITH_MASK": "maskelerle kesiştir",
+  "STYLUS": "Stylus",
+  "TOGGLE_PEN_MODE": "Kalem modunu aç/kapat",
+  "UNDO": "Geri Al",
+  "UNDO_DESCRIPTIVE": "Son işlemi geri al",
+  "REDO": "Yinele",
+  "REDO_DESCRIPTIVE": "Son işlemi yeniden yap",
+  "WINDOWS": "Windows",
+  "TOGGLE_GRIDLINES": "Kılavuz çizgilerini aç/kapat",
+  "ZOOM_IN": "Yakınlaştır",
+  "ZOOM_OUT": "Uzaklaştır",
+  "NEW_WINDOW_FOR_IMG": "Mevcut resim için yeni pencere aç",
+  "CENTER_ACTIVE_VIEWPORT": "Etkin görünümü ortala",
+  "FLIP_VIEWPORT_HORIZONTALLY": "Görünümü yatay olarak çevir",
+  "FLIP_VIEWPORT_VERTICALLY": "Görünümü dikey olarak çevir",
+  "SETTINGS": "Ayarlar",
+  "OPEN_SETTINGS": "Ayarları aç",
+  "OPEN_SETTINGS_DESCRIPTIVE": "Ayarlar penceresini aç",
+  "OPEN_STARTUP_WINDOW": "Başlangıç penceresini aç",
+  "OPEN_SHORTCUT_WINDOW": "Kısayol penceresini aç",
+  "OPEN_ABOUT_WINDOW": "Hakkında penceresini aç",
+  "OPEN_NAVIGATION_WINDOW": "Gezinme penceresini aç",
+  "ERROR": "Hata",
+  "INTERNAL_ERROR": "Dahili hata",
+  "ERROR_SAVE_LOCATION": "Dosyayı belirtilen konuma kaydedemedim",
+  "ERROR_WHILE_SAVING": "Kaydetme sırasında dahili bir hata oluştu. Lütfen tekrar deneyin.",
+  "UNKNOWN_ERROR_SAVING": "Kaydetme sırasında 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": "İsimsiz",
+  "OPEN_COMMAND_DEBUG_WINDOW": "Komut hata ayıklama penceresini aç",
+  "DELETE": "Sil",
+  "USER_PREFS": "Kullanıcı tercihleri (Gezinti)",
+  "SHORTCUT_FILE": "Kısayol dosyası (Gezinti)",
+  "EDITOR_DATA": "Düzenleyici verileri (Yerel)",
+  "MOVE_VIEWPORT_TOOLTIP": "Görünümü taşı. ({0})",
+  "MOVE_VIEWPORT_ACTION_DISPLAY": "Görünümü kaydırmak için tıklayın ve sürükleyin",
+  "MOVE_TOOL_TOOLTIP": "Seçili pikselleri taşır ({0}). Tüm katmanları taşımak için Ctrl tuşuna basılı tutun.",
+  "MOVE_TOOL_ACTION_DISPLAY": "Seçili pikselleri taşımak için fareyi basılı tutun. Tüm katmanları taşımak için Ctrl tuşuna basılı tutun.",
+  "PEN_TOOL_TOOLTIP": "Kalem. ({0})",
+  "PEN_TOOL_ACTION_DISPLAY": "Çizmek için tıklayın ve sürükleyin.",
+  "PIXEL_PERFECT_SETTING": "Mükemmel piksel",
+  "RECTANGLE_TOOL_TOOLTIP": "Tuval üzerinde dikdörtgen çizer ({0}). Kare çizmek için Shift tuşunu basılı tutun.",
+  "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT": "Dikdörtgen çizmek için tıklayın ve sürükleyin. Kare çizmek için Shift tuşunu basılı tutun.",
+  "RECTANGLE_TOOL_ACTION_DISPLAY_SHIFT": "Kare çizmek için tıklayın ve sürükleyin.",
+  "KEEP_ORIGINAL_IMAGE_SETTING": "Orijinal resmi koru",
+  "ROTATE_VIEWPORT_TOOLTIP": "Görünümü döndür. ({0})",
+  "ROTATE_VIEWPORT_ACTION_DISPLAY": "Görünümü döndürmek için tıklayın ve sürükleyin",
+  "SELECT_TOOL_TOOLTIP": "Alan seçer. ({0})",
+  "SELECT_TOOL_ACTION_DISPLAY_DEFAULT": "Bir alan seçmek için tıklayın ve sürükleyin. Mevcut seçime eklemek için Shift tuşunu basılı tutun. Çıkarmak için Ctrl tuşuna basılı tutun.",
+  "SELECT_TOOL_ACTION_DISPLAY_SHIFT": "Mevcut seçime eklemek için tıklayın ve sürükleyin.",
+  "SELECT_TOOL_ACTION_DISPLAY_CTRL": "Mevcut seçimden çıkarmak için tıklayın ve sürükleyin.",
+  "ZOOM_TOOL_TOOLTIP": "Görünümü yakınlaştırır ({0}). Yakınlaştırmak için tıklayın, alt tuşunu basılı tutun ve tıklayınarak uzaklaştırın.",
+  "ZOOM_TOOL_ACTION_DISPLAY_DEFAULT": "Yakınlaştırmak için tıklayın ve sürükleyin. Yakınlaştırmak için Ctrl tuşunu basılı tutun ve tıklayınarak uzaklaştırın.",
+  "ZOOM_TOOL_ACTION_DISPLAY_CTRL": "Yakınlaştırmak için tıklayın ve sürükleyin. Uzaklaştırmak için Ctrl tuşunu bırakın ve tıklayın.",
+  "BRIGHTNESS_TOOL_TOOLTIP": "Pikselleri daha parlak veya daha koyu hale getirir ({0}). Pikselleri daha koyu yapmak için Ctrl tuşunu basılı tutun.",
+  "BRIGHTNESS_TOOL_ACTION_DISPLAY_DEFAULT": "Pikselleri daha parlak hale getirmek için üzerlerine çizin. Pikselleri daha koyu yapmak için Ctrl tuşunu basılı tutun.",
+  "BRIGHTNESS_TOOL_ACTION_DISPLAY_CTRL": "Pikselleri daha koyu yapmak için üzerlerine çizin. Daha parlak hale getirmek 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ı gizlemek için Shift tuşunu basılı tutun",
+  "ELLIPSE_TOOL_TOOLTIP": "Tuval üzerinde 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 tıklayın ve sürükleyin. Bir daire çizmek için Shift tuşunu basılı tutun.",
+  "ELLIPSE_TOOL_ACTION_DISPLAY_SHIFT": "Bir daire çizmek için tıklayın ve sürükleyin.",
+  "ERASER_TOOL_TOOLTIP": "Piksellerden rengi siler. ({0})",
+  "ERASER_TOOL_ACTION_DISPLAY": "Silmek için tıklayın ve sürükleyin.",
+  "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. Yalnızca geçerli katmanları dikkate almak için Ctrl tuşunu bırakın.",
+  "LASSO_TOOL_TOOLTIP": "Lasso. ({0})",
+  "LASSO_TOOL_ACTION_DISPLAY_DEFAULT": "Lasso içindeki pikselleri seçmek için tıklayın ve sürükleyin. Mevcut seçime eklemek için Shift tuşunu basılı tutun. Çıkarmak için Ctrl tuşunu basılı tutun.",
+  "LASSO_TOOL_ACTION_DISPLAY_SHIFT": "Lasso içindeki pikselleri seçmek için tıklayın ve sürükleyin.",
+  "LASSO_TOOL_ACTION_DISPLAY_CTRL": "Lasso içindeki pikselleri mevcut seçimden çıkarmak için tıklayın ve sürükleyin.",
+  "LINE_TOOL_TOOLTIP": "Tuval üzerinde çizgi çizer ({0}). Yakalama etkinleştirmek için Shift tuşunu basılı tutun.",
+  "LINE_TOOL_ACTION_DISPLAY_DEFAULT": "Bir çizgi çizmek için tıklayın ve sürükleyin. Yakalamayı etkinleştirmek için Shift tuşunu basılı tutun.",
+  "LINE_TOOL_ACTION_DISPLAY_SHIFT": "Yakalama etkinleştirilmiş bir çizgi çizmek için tıklayın ve sürükleyin.",
+  "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": "Doldurma",
+  "LASSO_TOOL": "Lasso",
+  "LINE_TOOL": "Çizgi",
+  "MAGIC_WAND_TOOL": "Sihirli Değnek",
+  "MOVE_TOOL": "Taşı",
+  "MOVE_VIEWPORT_TOOL": "Görünümü Taşı",
+  "RECTANGLE_TOOL": "Dikdörtgen",
+  "ROTATE_VIEWPORT_TOOL": "Görünümü Döndür",
+  "SELECT_TOOL_NAME": "Seçim",
+  "ZOOM_TOOL": "Yakınlaştırma",
+  "SHAPE_LABEL": "Şekil",
+  "MODE_LABEL": "Mod",
+  "SCOPE_LABEL": "Kapsam",
+  "FILL_SHAPE_LABEL": "Şekli Doldur",
+  "FILL_COLOR_LABEL": "Renk Doldur",
+  "TOOL_SIZE_LABEL": "Araç Boyutu",
+  "STRENGTH_LABEL": "Güç",
+  "NEW": "Yeni",
+  "ADD": "Ekle",
+  "SUBTRACT": "Çıkar",
+  "INTERSECT": "Kesişim",
+  "RECTANGLE": "Dikdörtgen",
+  "CIRCLE": "Daire",
+  "ABOUT": "Hakkında",
+  "MINIMIZE": "Simge Durumuna Küçült",
+  "RESTORE": "Geri Al",
+  "MAXIMIZE": "Büyüt",
+  "CLOSE": "Kapat",
+  "EXPORT_SIZE_HINT": "Görüntüyü paylaşmak isterseniz, en iyi netlik için {0}% önerilir",
+  "CREATE": "Oluştur",
+  "BASE_LAYER_NAME": "Ana katman",
+  "ENABLE_MASK": "Maskeleri Etkinleştir",
+  "SELECTED_AREA_EMPTY": "Seçilen 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": "Görüntüyü 90° Döndür",
+  "ROT_IMG_180_D": "Görüntüyü 180° Döndür",
+  "ROT_IMG_-90_D": "Görüntüyü -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 rengi 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",
+  "NAVIGATION_TITLE": "Gezinme",
+  "NORMAL_BLEND_MODE": "Normal",
+  "DARKEN_BLEND_MODE": "Karartma",
+  "MULTIPLY_BLEND_MODE": "Çarpma",
+  "COLOR_BURN_BLEND_MODE": "Renk Yakma",
+  "LIGHTEN_BLEND_MODE": "Aydınlatma",
+  "SCREEN_BLEND_MODE": "Ekran",
+  "COLOR_DODGE_BLEND_MODE": "Renk Geçişi",
+  "OVERLAY_BLEND_MODE": "Üzerine Yerleştirme",
+  "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": "Desteklenmeyen",
+  "RESTART": "Yeniden Başlat",
+  "SORT_BY": "Sırala",
+  "NAME": "İsim",
+  "COLORS": "Renkler",
+  "DEFAULT": "Varsayılan",
+  "ALPHABETICAL": "Alfabetik",
+  "COLOR_COUNT": "Renk sayısı",
+  "ANY": "Herhangi biri",
+  "MAX": "Maksimum",
+  "MIN": "Minimum",
+  "EXACT": "Tam",
+  "ASCENDING": "Artan",
+  "DESCENDING": "Azalan",
+  "NAME_IS_TOO_LONG": "İsim çok uzun",
+  "STOP_IT_TEXT1": "Yeter. Dosya isimlerinizi düzgünleştirin.",
+  "STOP_IT_TEXT2": "Bu isimleri kopyalamayı bırakabilir misiniz?",
+  "REPLACER_TOOLTIP": "Palet renginin üzerine sağ tıklayın ve 'Değiştir' seçeneğini seçin veya buraya bırakın.",
+  "CLICK_TO_CHOOSE_COLOR": "Renk 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. Başka bir palet renginin üzerine sürükleyip bırakarak değiştirin.",
+  "ADD_FROM_SWATCHES": "Renk örneklerinden ekle",
+  "ADD_COLOR_TO_PALETTE": "Palet'e renk ekle",
+  "USE_IN_CURRENT_IMAGE": "Geçerli görüntüde kullan",
+  "ADD_TO_FAVORITES": "Favorilere ekle",
+  "BROWSE_PALETTES": "Paletlere göz at",
+  "LOAD_PALETTE": "Paleti 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",
+  "MIDDLE_RIGHT": "Sağ orta",
+  "BOTTOM_LEFT": "Sol alt",
+  "BOTTOM_CENTER": "Orta alt",
+  "BOTTOM_RIGHT": "Sağ alt",
+  "CLIP_TO_BELOW": "Alt katmana 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": "Palet alınamadı",
+  "NO_PALETTES_FOUND": "Palet bulunamadı.",
+  "LOSPEC_LINK_TEXT": "İşte bazılarına buradan ulaşabilirsiniz: 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} kaynaklı kısayollar başarıyla içe aktarıldı.",
+  "SHORTCUT_PROVIDER_DETECTED": "{0} yüklendiği tespit edildi. Kısayollarını içe aktarmak istiyor musunuz?",
+  "IMPORT_FROM_INSTALLATION": "Yüklemeyi içe aktar",
+  "IMPORT_INSTALLATION_OPTION1": "Yüklemeyi 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ı bozuldu, varsayılanlara sıfırlanıyor.",
+  "FAILED_DOWNLOAD_PALETTE": "Palet indirilemedi",
+  "FILE_INCORRECT_FORMAT": "Dosya doğru bir formatta değil",
+  "INVALID_FILE": "Geçersiz dosya",
+  "SHORTCUTS_FILE_INCORRECT_FORMAT": "Kısayollar dosyası doğru bir formatta değil",
+  "UNSUPPORTED_FILE_FORMAT": "Bu dosya formatı desteklenmiyor",
+  "ALREADY_ASSIGNED": "Zaten atanmış",
+  "REPLACE": "Değiştir",
+  "SWAP": "Değiştir",
+  "SHORTCUT_ALREADY_ASSIGNED_SWAP": "Bu kısayol zaten '{0}' için atanmış\nMevcut kısayolu değiştirmek veya ikisini değiştirmek istiyor musunuz?",
+  "SHORTCUT_ALREADY_ASSIGNED_OVERWRITE": "Bu kısayol zaten '{0}' için atanmış\nMevcut kısayolu değiştirmek istiyor musunuz?",
+  "UNSAVED_CHANGES": "Kaydedilmemiş değişiklikler",
+  "DOCUMENT_MODIFIED_SAVE": "Belge değiştirildi. Değişiklikleri kaydetmek istiyor musunuz?",
+  "SESSION_UNSAVED_DATA": "{0} kaydedilmemiş veri içeriyor. Emin misiniz?",
+  "PROJECT_MAINTAINERS": "Proje Bakımcıları",
+  "OTHER_AWESOME_CONTRIBUTORS": "Ve diğer harika katkıda bulunanlar",
+  "HELP": "Yardım",
+  "STOP_IT_TEXT3": "Hayır gerçekten, yeter.",
+  "STOP_IT_TEXT4": "Daha iyi yapacak başka bir şeyiniz yok mu?",
+  "LINEAR_DODGE_BLEND_MODE": "Lineer dodge (Ekleme)",
+  "PRESS_ANY_KEY": "Herhangi bir tuşa basın",
+  "NONE_SHORTCUT": "Hiçbiri",
+  "REFERENCE": "Referans",
+  "PUT_REFERENCE_LAYER_ABOVE": "Referans katmanını üste yerleştir",
+  "PUT_REFERENCE_LAYER_BELOW": "Referans katmanını alta yerleştir",
+  "TOGGLE_VERTICAL_SYMMETRY": "Dikey simetriyi aç/kapat",
+  "TOGGLE_HORIZONTAL_SYMMETRY": "Yatay simetriyi aç/kapat",
+  "RESET_VIEWPORT": "Görünümü sıfırla",
+  "VIEWPORT_SETTINGS": "Görünüm ayarları",
+  "MOVE_TOOL_ACTION_DISPLAY_TRANSFORMING": "Tıklanıp basılı tutarak seçili katmanlardaki pikselleri taşımak için fareyi kullanın.",
+  "MOVE_TOOL_ACTION_DISPLAY_CTRL": "Tüm katmanları taşımak için fareyi 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": "Hata: {0}",
+  "DROP_PALETTE": "Paleti Buraya Bırak",
+  "SECURITY_ERROR": "Güvenlik Hatası",
+  "SECURITY_ERROR_MSG": "Belirtilen konuma yazma izni yok.",
+  "IO_ERROR": "G/Ç Hatası",
+  "IO_ERROR_MSG": "Disk'e 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 bulunmuyor.",
+  "ALL_LAYERS": "Tüm Katmanlar",
+  "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": "Dosya türü '{0}' 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 palet içine aktar",
+  "IMPORT_SINGLE_PALETTE_COLOR": "Renkleri palet içine aktar",
+  "IMPORT_AS_REFERENCE_LAYER": "Referans katmanı olarak içe aktar",
+  "NAVIGATOR_PICK_ACTION_DISPLAY": "Renk seçmek için sağ tıklayın, Renkleri panoya kopyalamak için Shift tuşuna basılı tutun",
+  "OPEN_FILE_FROM_CLIPBOARD": "Panodan aç",
+  "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE": "Panodan aç",
+  "OPEN_LOCALIZATION_DATA": "LocalizationData.json dosyasını mı açmak istiyorsunuz?\nGüncellenmiş tarih panoya kopyalandı.\nNot Değişiklikler, yeniden başlatma yapılana kadar uygulanmayacaktır",
+  "DOWNLOADING_LANGUAGE_FAILED": "Dil indirme başarısız oldu.\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ı.\n'{0}' dosyası aranıyor",
+  "PROJECT_ROOT_NOT_FOUND": "PixiEditor proje kökü bulunamadı.\nPixiEditor.csproj dosyası aranıyor",
+  "LOCALIZATION_FOLDER_NOT_FOUND": "Yerelleştirme klasörü bulunamadı.\n/Data/Localization dizini aranıyor",
+  "SELECT_A_LANGUAGE": "Bir dil seçin",
+  "DONE": "Tamamlandı",
+  "SOURCE_UNSET_OR_MISSING": "Kaynak eksik/belirtilmemiş",
+  "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 renk seçmek için tıklayın.",
+  "COLOR_PICKER_ACTION_DISPLAY_CANVAS_ONLY": "Tuvalden renk 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üştürmeyi ölçeklendirmek için kolları sürükleyin. Bir kolun üzerine tıklayıp Ctrl tuşunu basılı tutarak kolun serbestçe hareket etmesini sağlayın. Orantılı olarak ölçeklendirmek için Shift tuşunu basılı tutun. Bir yan kolun üzerine tıklayıp Alt tuşunu basılı tutarak kaydırın. Dış kolları sürükleyerek döndürün.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "Dönüştürmeyi ölçeklendirmek için kolları sürükleyin. Orantılı olarak ölçeklendirmek için Shift tuşunu basılı tutun. Bir yan kolun üzerine tıklayıp Alt tuşunu basılı tutarak kaydırın. Dış kolları sürükleyerek döndürün.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "Dönüştürmeyi ölçeklendirmek için kolları sürükleyin. Shift tuşunu basılı tutunarak orantılı olarak ölçeklendirin. Dış kolları sürükleyerek döndürün.",
+  "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "Dönüştürmeyi ölçeklendirmek için kolları sürükleyin. Shift tuşunu basılı tutarak orantılı olarak ölçeklendirin.",
+  "OPEN_DOCUMENTATION": "Dokümantasyonu aç",
+  "LOCAL_PALETTE_SOURCE_NAME": "Yerel",
+  "ERROR_FORBIDDEN_UNIQUE_NAME": "Uzantı benzersiz adı 'pixieditor' ile başlayamaz.",
+  "ERROR_MISSING_METADATA": "Uzantı metaverisi '{0}' eksik.",
+  "ERROR_NO_CLASS_ENTRY": "Uzantı sınıf girişi '{0}' yolda eksik.",
+  "ERROR_NO_ENTRY_ASSEMBLY": "Uzantı giriş derlemesi '{0}' yolda eksik.",
+  "ERROR_MISSING_ADDITIONAL_CONTENT": "Mevcut yapılandırmanız bu uzantının yüklenmesine izin vermiyor. Belki de sahibi değilsiniz veya yüklü değil. Uzantıyı buradan satın alabilirsiniz '{0}'.",
+  "BUY_SUPPORTER_PACK": "Destekçi Paketi Satın Al",
+  "NEWS": "Haberler",
+  "DISABLE_NEWS_PANEL": "Başlangıç penceresinde Haberler panelini devre dışı bırak",
+  "FAILED_FETCH_NEWS": "Haberleri alırken hata oluştu",
+  "CROP_TO_SELECTION": "Seçime Kırp",
+  "CROP_TO_SELECTION_DESCRIPTIVE": "Resmi seçime 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",
+  "COPY_COLOR": "Rengi Kopyala",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Couldn't open File Explorer",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Could not recover all",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Could not fully recover all files.\nIf you send the crash report to the developers\nthey might be able to help you."
 }

+ 4 - 1
src/PixiEditor/Data/Localization/Languages/uk.json

@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "Режим правої кнопки миші",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Додати основний колір до палітри",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Додати основний колір до поточної палітри",
-  "COPY_COLOR": "Copy color"
+  "COPY_COLOR": "Копія кольору",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Не вдалося відкрити Провідник",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Не вдалося відновити все",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Не вдалося повністю відновити всі файли.\nЯкщо ви надішлете звіт про збій розробникам\nвони можуть вам допомогти."
 }

+ 19 - 16
src/PixiEditor/Data/Localization/Languages/zh.json

@@ -1,8 +1,8 @@
 {
   "RECENT_FILES": "最近",
-  "OPEN_FILE": "打开",
+  "OPEN_FILE": "打开文件",
   "NEW_FILE": "新建",
-  "RECENT_EMPTY_TEXT": "空空如也……",
+  "RECENT_EMPTY_TEXT": "这里空空如也……",
   "LANGUAGE": "语言",
   "GENERAL": "基础设置",
   "DISCORD": "Discord",
@@ -88,7 +88,7 @@
   "LOCATION_DOES_NOT_EXIST": "位置不存在。",
   "FILE_NOT_FOUND": "无法找到文件。",
   "ARE_YOU_SURE": "你确定吗?",
-  "ARE_YOU_SURE_PATH_FULL_PATH": "你确定要删除{0}吗?\n所有安装都将丢失此资料。\n(完整位置:{1})",
+  "ARE_YOU_SURE_PATH_FULL_PATH": "你确定要删除{0}吗?\n所有安装都将丢失此资料。\n(完整路径:{1})",
   "FAILED_TO_OPEN_FILE": "无法打开文件",
   "OLD_FILE_FORMAT": "旧文件格式",
   "OLD_FILE_FORMAT_DESCRIPTION": "此 .pixi 文件使用了已被停止支持的旧文件格式。",
@@ -325,7 +325,7 @@
   "COLOR_PICKER_TOOL": "选色器",
   "ELLIPSE_TOOL": "椭圆",
   "ERASER_TOOL": "橡皮",
-  "FLOOD_FILL_TOOL": "填满",
+  "FLOOD_FILL_TOOL": "颜料桶",
   "LASSO_TOOL": "拉索",
   "LINE_TOOL": "直线",
   "MAGIC_WAND_TOOL": "魔法棒",
@@ -537,7 +537,7 @@
   "NAVIGATOR_PICK_ACTION_DISPLAY": "右键以选择颜色,按住Shift时右键以将其复制至剪贴薄",
   "OPEN_FILE_FROM_CLIPBOARD": "从剪贴薄中打开",
   "OPEN_FILE_FROM_CLIPBOARD_DESCRIPTIVE": "从剪贴薄中打开文件",
-  "OPEN_LOCALIZATION_DATA": "是否打开 LocalizationData.json?更新日期已被加入至剪贴板。\n注意更改只会在重启此程序后启用。",
+  "OPEN_LOCALIZATION_DATA": "是否打开 LocalizationData.json?\n更新日期已被加入至剪贴板。\n注意更改只会在重启此程序后启用。",
   "DOWNLOADING_LANGUAGE_FAILED": "无法下载语言。\nAPI密码可能被过度使用。",
   "LOCALIZATION_DATA_NOT_FOUND": "无法找到本地化资料路径",
   "APPLY": "应用",
@@ -561,17 +561,17 @@
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_SHEAR_NOPERSPECTIVE": "拖动手柄以缩放已选像素。按住 Shift 键按比例缩放。按住 Alt 并拖动侧手柄进行剪切。拖动外部手柄进行旋转。",
   "TRANSFORM_ACTION_DISPLAY_SCALE_ROTATE_NOSHEAR_NOPERSPECTIVE": "拖动手柄以缩放已选像素。按住 Shift 键按比例缩放。拖动外部手柄进行旋转。",
   "TRANSFORM_ACTION_DISPLAY_SCALE_NOROTATE_NOSHEAR_NOPERSPECTIVE": "拖动手柄以缩放已选像素。按住 Shift 键按比例缩放。",
-  "OPEN_DOCUMENTATION": "Open documentation",
-  "LOCAL_PALETTE_SOURCE_NAME": "Local",
+  "OPEN_DOCUMENTATION": "打开文档",
+  "LOCAL_PALETTE_SOURCE_NAME": "本地",
   "ERROR_FORBIDDEN_UNIQUE_NAME": "Extension unique name cannot start with 'pixieditor'.",
-  "ERROR_MISSING_METADATA": "Extension metadata key '{0}' is missing.",
-  "ERROR_NO_CLASS_ENTRY": "Extension class entry is missing on path '{0}'.",
-  "ERROR_NO_ENTRY_ASSEMBLY": "Extension entry assembly is missing on path '{0}'.",
-  "ERROR_MISSING_ADDITIONAL_CONTENT": "Your current setup doesn't allow loading this extension. Perhaps you don't own it or don't have it installed. You can purchase it here '{0}'.",
-  "BUY_SUPPORTER_PACK": "Buy Supporter Pack",
-  "NEWS": "News",
-  "DISABLE_NEWS_PANEL": "Disable News panel in startup window",
-  "FAILED_FETCH_NEWS": "Failed to fetch news",
+  "ERROR_MISSING_METADATA": "扩展元数据键 “{0}” 丢失。",
+  "ERROR_NO_CLASS_ENTRY": "路径“{0}”上缺少扩展类程序。",
+  "ERROR_NO_ENTRY_ASSEMBLY": "路径“{0}”上缺少扩展程序集。",
+  "ERROR_MISSING_ADDITIONAL_CONTENT": "你当前的设置不允许加载此扩展。 也许你不拥有它或没有安装它。 你可以在“{0}”处购买。",
+  "BUY_SUPPORTER_PACK": "购买支持者包",
+  "NEWS": "新消息",
+  "DISABLE_NEWS_PANEL": "禁用开始窗口的新消息页面",
+  "FAILED_FETCH_NEWS": "无法获取新消息",
   "CROP_TO_SELECTION": "Crop to selection",
   "CROP_TO_SELECTION_DESCRIPTIVE": "Crop image to selection",
   "SHOW_CONTEXT_MENU": "Show context menu",
@@ -580,5 +580,8 @@
   "RIGHT_CLICK_MODE": "Right click mode",
   "ADD_PRIMARY_COLOR_TO_PALETTE": "Add primary color to palette",
   "ADD_PRIMARY_COLOR_TO_PALETTE_DESCRIPTIVE": "Add primary color to current palette",
-  "COPY_COLOR": "Copy color"
+  "COPY_COLOR": "Copy color",
+  "ERROR_FAILED_TO_OPEN_EXPLORER": "Couldn't open File Explorer",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE": "Could not recover all",
+  "CRASH_NOT_ALL_DOCUMENTS_RECOVERED": "Could not fully recover all files.\nIf you send the crash report to the developers\nthey might be able to help you."
 }

+ 9 - 9
src/PixiEditor/Data/Localization/LocalizationData.json

@@ -13,14 +13,14 @@
       "code": "pl",
       "localeFileName": "pl.json",
       "iconFileName": "pl.png",
-      "lastUpdated": "2023-06-25 14:16:00"
+      "lastUpdated": "2023-12-14 16:59:56"
     },
     {
       "name": "Deutsch",
       "code": "de",
       "localeFileName": "de.json",
       "iconFileName": "de.png",
-      "lastUpdated": "2023-06-25 12:55:11"
+      "lastUpdated": "2023-12-14 16:40:04"
     },
     {
       "name": "Español",
@@ -34,21 +34,21 @@
       "code": "zh",
       "localeFileName": "zh.json",
       "iconFileName": "zh.png",
-      "lastUpdated": "2023-06-01 12:32:02"
+      "lastUpdated": "2023-08-14 07:59:43"
     },
     {
       "name": "Русский",
       "code": "ru",
       "localeFileName": "ru.json",
       "iconFileName": "ru.png",
-      "lastUpdated": "2023-06-25 14:17:44"
+      "lastUpdated": "2023-12-14 16:54:15"
     },
     {
       "name": "Українська",
       "code": "uk",
       "localeFileName": "uk.json",
       "iconFileName": "uk.png",
-      "lastUpdated": "2023-06-25 11:35:31"
+      "lastUpdated": "2023-12-14 16:56:58"
     },
     {
       "name": "عربي",
@@ -56,21 +56,21 @@
       "localeFileName": "ar.json",
       "iconFileName": "ar.png",
       "rightToLeft": true,
-      "lastUpdated": "2023-06-25 14:47:47"
+      "lastUpdated": "2023-12-14 17:25:23"
     },
     {
       "name": "Čeština",
       "code": "cs",
       "localeFileName": "cs.json",
       "iconFileName": "cs.png",
-      "lastUpdated": "2023-06-27 18:55:52"
+      "lastUpdated": "2023-12-14 20:10:32"
     },
     {
       "name": "Português (Brasil)",
       "code": "pt-br",
       "localeFileName": "pt-br.json",
       "iconFileName": "pt-br.png",
-      "lastUpdated": "2023-06-01 21:22:27"
+      "lastUpdated": "2023-07-06 04:00:53"
     },
     {
       "name": "Magyar",
@@ -91,7 +91,7 @@
       "code": "tr",
       "localeFileName": "tr.json",
       "iconFileName": "tr.png",
-      "lastUpdated": "2023-06-25 19:43:24"
+      "lastUpdated": "2023-06-29 22:44:34"
     }
   ]
 }

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

@@ -8,8 +8,13 @@ internal class ReciprocalConverter : SingleInstanceConverter<ReciprocalConverter
     {
         if (value is not double num)
             return DependencyProperty.UnsetValue;
+
+        double result;
         if (parameter is not double mult)
-            return 1 / num;
-        return mult / num;
+            result = 1 / num;
+        else
+            result = mult / num;
+
+        return Math.Clamp(result, 1e-15, 1e15);
     }
 }

+ 13 - 3
src/PixiEditor/Helpers/CrashHelper.cs

@@ -112,14 +112,24 @@ internal class CrashHelper
         }
     }
 
-    public static async Task SendExceptionInfoToWebhook(Exception e, [CallerFilePath] string filePath = "<unknown>", [CallerMemberName] string memberName = "<unknown>")
+    public static void SendExceptionInfoToWebhook(Exception e, bool wait = false,
+        [CallerFilePath] string filePath = "<unknown>", [CallerMemberName] string memberName = "<unknown>")
+    {
+        var task = Task.Run(() => SendExceptionInfoToWebhookAsync(e, filePath, memberName));
+        if (wait)
+        {
+            task.Wait();
+        }
+    }
+
+    public static async Task SendExceptionInfoToWebhookAsync(Exception e, [CallerFilePath] string filePath = "<unknown>", [CallerMemberName] string memberName = "<unknown>")
     {
         if (DebugViewModel.IsDebugBuild)
             return;
-        await SendReportTextToWebhook(CrashReport.Generate(e), $"{filePath}; Method {memberName}");
+        await SendReportTextToWebhookAsync(CrashReport.Generate(e), $"{filePath}; Method {memberName}");
     }
 
-    public static async Task SendReportTextToWebhook(CrashReport report, string catchLocation = null)
+    public static async Task SendReportTextToWebhookAsync(CrashReport report, string catchLocation = null)
     {
         string reportText = report.ReportText;
         if (catchLocation is not null)

+ 5 - 1
src/PixiEditor/Helpers/ProcessHelper.cs

@@ -3,6 +3,7 @@ using System.Diagnostics;
 using System.IO;
 using System.Security.Principal;
 using System.Windows.Input;
+using PixiEditor.Exceptions;
 
 namespace PixiEditor.Helpers;
 
@@ -32,10 +33,13 @@ internal static class ProcessHelper
         {
             string fixedPath = Path.GetFullPath(path);
             var process = Process.Start("explorer.exe", $"/select,\"{fixedPath}\"");
-
             // Explorer might need a second to show up
             process.WaitForExit(500);
         }
+        catch (Win32Exception)
+        {
+            throw new RecoverableException("ERROR_FAILED_TO_OPEN_EXPLORER");
+        }
         finally
         {
             Mouse.OverrideCursor = null;

+ 2 - 2
src/PixiEditor/Models/AppExtensions/ExtensionLoader.cs

@@ -49,7 +49,7 @@ internal class ExtensionLoader
         }
         catch (Exception ex)
         {
-            Task.Run(async () => await CrashHelper.SendExceptionInfoToWebhook(ex));
+            CrashHelper.SendExceptionInfoToWebhook(ex);
         }
     }
 
@@ -86,7 +86,7 @@ internal class ExtensionLoader
         catch (Exception ex)
         {
             //MessageBox.Show(new LocalizedString("ERROR_LOADING_PACKAGE", packageJsonPath), "ERROR");
-            Task.Run(async () => await CrashHelper.SendExceptionInfoToWebhook(ex));
+            CrashHelper.SendExceptionInfoToWebhook(ex);
         }
     }
 

+ 3 - 0
src/PixiEditor/Models/Commands/CommandController.cs

@@ -25,6 +25,8 @@ internal class CommandController
     public CommandCollection Commands { get; }
 
     public List<CommandGroup> CommandGroups { get; }
+    
+    public CommandLog.CommandLog Log { get; }
 
     public OneToManyDictionary<string, Command> FilterCommands { get; }
     
@@ -37,6 +39,7 @@ internal class CommandController
     public CommandController()
     {
         Current ??= this;
+        Log = new CommandLog.CommandLog();
 
         ShortcutsPath = Path.Join(
             Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),

+ 43 - 0
src/PixiEditor/Models/Commands/CommandLog/CommandLog.cs

@@ -0,0 +1,43 @@
+using System.Globalization;
+using System.Text;
+using PixiEditor.Models.Commands.Commands;
+
+namespace PixiEditor.Models.Commands.CommandLog;
+
+internal class CommandLog
+{
+    private readonly List<CommandLogEntry> list = new(MaxEntries);
+
+    private const int MaxEntries = 8;
+
+    public void Log(Command command, bool? canExecute)
+    {
+        if (canExecute.HasValue && !list[0].CanExecute.HasValue)
+        {
+            list[0].CanExecute = canExecute;
+            return;
+        }
+        
+        if (list.Count >= MaxEntries)
+        {
+            list.RemoveRange(MaxEntries - 1, list.Count - MaxEntries + 1);
+        }
+        
+        list.Insert(0, new CommandLogEntry(command, canExecute, DateTime.Now));
+    }
+
+    public string GetSummary(DateTime relativeTime)
+    {
+        var builder = new StringBuilder();
+
+        foreach (var entry in list)
+        {
+            var relativeSpan = entry.DateTime - relativeTime;
+            string canExecute = entry.CanExecute.HasValue ? entry.CanExecute.ToString() : "not executed";
+            
+            builder.AppendLine($"{entry.Command.InternalName} | CanExecute: {canExecute} | {relativeSpan.TotalSeconds.ToString("F3", CultureInfo.InvariantCulture)}s ago | {entry.DateTime.ToString("O", CultureInfo.InvariantCulture)}");
+        }
+
+        return builder.ToString();
+    }
+}

+ 19 - 0
src/PixiEditor/Models/Commands/CommandLog/CommandLogEntry.cs

@@ -0,0 +1,19 @@
+using PixiEditor.Models.Commands.Commands;
+
+namespace PixiEditor.Models.Commands.CommandLog;
+
+internal class CommandLogEntry
+{
+    public Command Command { get; }
+
+    public bool? CanExecute { get; set; }
+    
+    public DateTime DateTime { get; }
+    
+    public CommandLogEntry(Command command, bool? commandMethod, DateTime dateTime)
+    {
+        Command = command;
+        CanExecute = commandMethod;
+        DateTime = dateTime;
+    }
+}

+ 17 - 2
src/PixiEditor/Models/Commands/CommandMethods.cs

@@ -18,11 +18,26 @@ internal class CommandMethods
 
     public void Execute(object parameter)
     {
-        if (CanExecute(parameter))
+        var log = CommandController.Current?.Log;
+        ToLog(log, null);
+        
+        if (!CanExecute(parameter))
         {
-            _execute(parameter);
+            ToLog(log, false);
+            return;
         }
+        ToLog(log, true);
+
+        _execute(parameter);
     }
 
     public bool CanExecute(object parameter) => _canExecute.CallEvaluate(_command, parameter);
+
+    private void ToLog(CommandLog.CommandLog? log, bool? canExecute)
+    {
+        if (log != null && _command != null)
+        {
+            log.Log(_command, canExecute);
+        }
+    }
 }

+ 99 - 21
src/PixiEditor/Models/Controllers/MouseUpdateController.cs

@@ -1,48 +1,126 @@
-using System.Timers;
+using System.Diagnostics;
 using System.Windows;
 using System.Windows.Input;
 using System.Windows.Threading;
 
 namespace PixiEditor.Models.Controllers;
 
+#nullable enable
 public class MouseUpdateController : IDisposable
 {
-    private const int MouseUpdateIntervalMs = 7;  // 7ms ~= 142 Hz
-    
-    private readonly System.Timers.Timer _timer;
-    
-    private UIElement element;
-    
-    private MouseEventHandler mouseMoveHandler;
+    private const double MouseUpdateIntervalMs = 1000 / 142.0; //142 Hz
+
+    private Thread timerThread;
+    private readonly AutoResetEvent resetEvent = new(false);
+    private readonly object lockObj = new();
+    private bool isAborted = false;
+
+    private readonly FrameworkElement element;
+    private readonly MouseEventHandler mouseMoveHandler;
     
-    public MouseUpdateController(UIElement uiElement, MouseEventHandler onMouseMove)
+    public MouseUpdateController(FrameworkElement uiElement, MouseEventHandler onMouseMove)
     {
         mouseMoveHandler = onMouseMove;
         element = uiElement;
-        
-        _timer = new System.Timers.Timer(MouseUpdateIntervalMs);
-        _timer.AutoReset = true;
-        _timer.Elapsed += TimerOnElapsed;
-        
         element.MouseMove += OnMouseMove;
+
+        bool wasThreadCreated = !element.IsLoaded;
+        element.Loaded += (_, _) =>
+        {
+            wasThreadCreated = true;
+            CreateTimerThread();
+        };
+
+        if (!wasThreadCreated)
+            CreateTimerThread();
+
+        element.Unloaded += (_, _) =>
+        {
+            isAborted = true;
+        };
     }
 
-    private void TimerOnElapsed(object sender, ElapsedEventArgs e)
+    private void CreateTimerThread()
     {
-        _timer.Stop();
-        element.MouseMove += OnMouseMove;
+        timerThread = new Thread(TimerThread);
+        timerThread.Name = "MouseUpdateController thread";
+        timerThread.Start();
+        isAborted = false;
+    }
+
+    private bool IsThreadShouldStop()
+    {
+        return isAborted || timerThread != Thread.CurrentThread || Application.Current is null;
+    }
+    
+    private void TimerThread()
+    {
+        try
+        {
+            long lastThreadIter = Stopwatch.GetTimestamp();
+            
+            // abort if a new thread was created
+            while (!IsThreadShouldStop())
+            {
+                // call waitOne periodically instead of waiting infinitely to make sure we crash or exit when resetEvent is disposed
+                if (!resetEvent.WaitOne(300))
+                {
+                    lastThreadIter = Stopwatch.GetTimestamp();
+                    continue;
+                }
+
+                lock (lockObj)
+                {
+                    double sleepDur = Math.Clamp(MouseUpdateIntervalMs - Stopwatch.GetElapsedTime(lastThreadIter).TotalMilliseconds, 0, MouseUpdateIntervalMs);
+                    lastThreadIter += (long)(MouseUpdateIntervalMs * Stopwatch.Frequency / 1000);
+                    if (sleepDur > 0)
+                        Thread.Sleep((int)Math.Round(sleepDur));
+                    
+                    if (IsThreadShouldStop())
+                        return;
+                    Application.Current?.Dispatcher.Invoke(() =>
+                    {
+                        element.MouseMove += OnMouseMove;
+                    });
+                    
+                }
+            }
+        }
+        catch (ObjectDisposedException)
+        {
+            return;
+        }
+        catch (Exception e)
+        {
+            Application.Current?.Dispatcher.BeginInvoke(() => throw new AggregateException("Input handling thread died", e), DispatcherPriority.SystemIdle);
+            throw;
+        }
     }
 
     private void OnMouseMove(object sender, MouseEventArgs e)
     {
-        element.MouseMove -= OnMouseMove;
-        _timer.Start();
-        mouseMoveHandler(sender, e);
+        bool lockWasTaken = false;
+        try
+        {
+            Monitor.TryEnter(lockObj, ref lockWasTaken);
+            if (lockWasTaken)
+            {
+                resetEvent.Set();
+                element.MouseMove -= OnMouseMove;
+                mouseMoveHandler(sender, e);
+            }
+        }
+        finally
+        {
+            if (lockWasTaken)
+                Monitor.Exit(lockObj);
+        }
     }
 
     public void Dispose()
     {
-        _timer.Dispose();
         element.MouseMove -= OnMouseMove;
+        isAborted = true;
+        resetEvent.Dispose();
     }
 }

+ 263 - 39
src/PixiEditor/Models/DataHolders/CrashReport.cs

@@ -1,12 +1,19 @@
 using System.Diagnostics;
+using System.Globalization;
 using System.IO;
 using System.IO.Compression;
 using System.Reflection;
 using System.Text;
+using System.Windows.Input;
 using Newtonsoft.Json;
+using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Extensions.Common.UserPreferences;
 using PixiEditor.Helpers;
+using PixiEditor.Models.Commands;
+using PixiEditor.Models.Enums;
 using PixiEditor.Parser;
 using PixiEditor.ViewModels.SubViewModels.Document;
+using PixiEditor.Views;
 
 namespace PixiEditor.Models.DataHolders;
 
@@ -20,14 +27,59 @@ internal class CrashReport : IDisposable
 
         builder
             .AppendLine($"PixiEditor {VersionHelpers.GetCurrentAssemblyVersionString(moreSpecific: true)} x{IntPtr.Size * 8} crashed on {currentTime:yyyy.MM.dd} at {currentTime:HH:mm:ss} {currentTime:zzz}")
+            .AppendLine($"Application started {GetFormatted(() => Process.GetCurrentProcess().StartTime, "yyyy.MM.dd HH:hh:ss")}, {GetFormatted(() => DateTime.Now - Process.GetCurrentProcess().StartTime, @"d\ hh\:mm\.ss")} ago")
             .AppendLine($"Report: {Guid.NewGuid()}\n")
             .AppendLine("-----System Information----")
             .AppendLine("General:")
             .AppendLine($"  OS: {Environment.OSVersion.VersionString}")
+            .AppendLine($"  Has Stylus Tablet Device: {GetFormatted(() => HasTabletDevice(TabletDeviceType.Stylus))}")
+            .AppendLine($"  Has Touch Tablet Device: {GetFormatted(() => HasTabletDevice(TabletDeviceType.Touch))}")
             .AppendLine();
 
         CrashHelper helper = new();
 
+        AppendHardwareInfo(helper, builder);
+
+        builder.AppendLine("\n--------Command Log--------\n");
+
+        try
+        {
+            builder.Append(CommandController.Current.Log.GetSummary(currentTime.LocalDateTime));
+        }
+        catch (Exception cemLogException)
+        {
+            builder.AppendLine($"Error ({cemLogException.GetType().FullName}: {cemLogException.Message}) while gathering command log, skipping...");
+        }
+        
+        builder.AppendLine("\n-----------State-----------");
+
+        try
+        {
+            AppendStateInfo(builder);
+        }
+        catch (Exception stateException)
+        {
+            builder.AppendLine($"Error ({stateException.GetType().FullName}: {stateException.Message}) while gathering state (Must be bug in GetPreferenceFormatted, GetFormatted or StringBuilder.AppendLine as these should not throw), skipping...");
+        }
+        
+        CrashHelper.AddExceptionMessage(builder, exception);
+
+        string filename = $"crash-{currentTime:yyyy-MM-dd_HH-mm-ss_fff}.zip";
+        string path = Path.Combine(
+            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
+            "PixiEditor",
+            "crash_logs");
+        Directory.CreateDirectory(path);
+
+        CrashReport report = new();
+        report.FilePath = Path.Combine(path, filename);
+        report.ReportText = builder.ToString();
+
+        return report;
+    }
+
+    private static void AppendHardwareInfo(CrashHelper helper, StringBuilder builder)
+    {
         try
         {
             helper.GetCPUInformation(builder);
@@ -54,21 +106,146 @@ internal class CrashReport : IDisposable
         {
             builder.AppendLine($"Error ({memE.GetType().FullName}: {memE.Message}) while gathering memory information, skipping...");
         }
+    }
 
-        CrashHelper.AddExceptionMessage(builder, exception);
+    private static void AppendStateInfo(StringBuilder builder)
+    {
+        builder
+            .AppendLine("Environment:")
+            .AppendLine($"  Thread Count: {GetFormatted(() => Process.GetCurrentProcess().Threads.Count)}")
+            .AppendLine("\nCulture:")
+            .AppendLine($"  Selected language: {GetPreferenceFormatted("LanguageCode", true, "system")}")
+            .AppendLine($"  Current Culture: {GetFormatted(() => CultureInfo.CurrentCulture)}")
+            .AppendLine($"  Current UI Culture: {GetFormatted(() => CultureInfo.CurrentUICulture)}")
+            .AppendLine("\nPreferences:")
+            .AppendLine($"  Has shared toolbar enabled: {GetPreferenceFormatted("EnableSharedToolbar", true, false)}")
+            .AppendLine($"  Right click mode: {GetPreferenceFormatted<RightClickMode>("RightClickMode", true)}")
+            .AppendLine($"  Has Rich presence enabled: {GetPreferenceFormatted("EnableRichPresence", true, true)}")
+            .AppendLine($"  Debug Mode enabled: {GetPreferenceFormatted("IsDebugModeEnabled", true, false)}")
+            .AppendLine("\nUI:")
+            .AppendLine($"  MainWindow not null: {GetFormatted(() => MainWindow.Current != null)}")
+            .AppendLine($"  MainWindow Size: {GetFormatted(() => MainWindow.Current.RenderSize)}")
+            .AppendLine($"  MainWindow State: {GetFormatted(() => MainWindow.Current.WindowState)}")
+            .AppendLine("\nViewModels:")
+            .AppendLine($"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.UpdateableChangeActive)}")
+            .AppendLine($"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
+            .AppendLine($"  Primary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.PrimaryColor)}")
+            .AppendLine($"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
+            .Append("\nActive Document: ");
 
-        string filename = $"crash-{currentTime:yyyy-MM-dd_HH-mm-ss_fff}.zip";
-        string path = Path.Combine(
-            Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
-            "PixiEditor",
-            "crash_logs");
-        Directory.CreateDirectory(path);
+        try
+        {
+            AppendActiveDocumentInfo(builder);
+        }
+        catch (Exception e)
+        {
+            builder.AppendLine($"Could not get active document info:\n{e}");
+        }
+    }
 
-        CrashReport report = new();
-        report.FilePath = Path.Combine(path, filename);
-        report.ReportText = builder.ToString();
+    private static void AppendActiveDocumentInfo(StringBuilder builder)
+    {
+        var main = ViewModelMain.Current;
+        
+        if (main == null)
+        {
+            builder.AppendLine("{ ViewModelMain.Current is null }");
+            return;
+        }
 
-        return report;
+        var manager = main.DocumentManagerSubViewModel;
+
+        if (manager == null)
+        {
+            builder.AppendLine("{ DocumentManagerSubViewModel is null }");
+            return;
+        }
+
+        var document = manager.ActiveDocument;
+
+        if (document == null)
+        {
+            builder.AppendLine("null");
+            return;
+        }
+
+        builder
+            .AppendLine()
+            .AppendLine($"  Size: {document.SizeBindable}")
+            .AppendLine($"  Layer Count: {FormatObject(document.StructureHelper.GetAllLayers().Count)}")
+            .AppendLine($"  Has all changes saved: {document.AllChangesSaved}")
+            .AppendLine($"  Horizontal Symmetry Enabled: {document.HorizontalSymmetryAxisEnabledBindable}")
+            .AppendLine($"  Horizontal Symmetry Value: {FormatObject(document.HorizontalSymmetryAxisYBindable)}")
+            .AppendLine($"  Vertical Symmetry Enabled: {document.VerticalSymmetryAxisEnabledBindable}")
+            .AppendLine($"  Vertical Symmetry Value: {FormatObject(document.VerticalSymmetryAxisXBindable)}")
+            .AppendLine($"  Updateable Change Active: {FormatObject(document.UpdateableChangeActive)}")
+            .AppendLine($"  Transform: {FormatObject(document.TransformViewModel)}");
+    }
+
+    private static bool HasTabletDevice(TabletDeviceType type) => Tablet.TabletDevices.Cast<TabletDevice>().Any(tabletDevice => tabletDevice.Type == type);
+
+    private static string GetPreferenceFormatted<T>(string name, bool roaming, T defaultValue = default, string? format = null)
+    {
+        try
+        {
+            var preferences = IPreferences.Current;
+
+            if (preferences == null)
+                return "{ Preferences are null }";
+            
+            var value = roaming
+                ? preferences.GetPreference(name, defaultValue)
+                : preferences.GetLocalPreference(name, defaultValue);
+
+            return FormatObject(value, format);
+        }
+        catch (Exception e)
+        {
+            return $$"""{ Failed getting preference: {{e.Message}} }""";
+        }
+    }
+
+    private static string GetFormattedFromViewModelMain<T>(Func<ViewModelMain, T?> getter, string? format = null)
+    {
+        var main = ViewModelMain.Current;
+        
+        if (main == null)
+            return "{ ViewModelMain.Current is null }";
+
+        return GetFormatted(() => getter(main), format);
+    }
+
+    private static string GetFormatted<T>(Func<T?> getter, string? format = null)
+    {
+        try
+        {
+            var value = getter();
+
+            return FormatObject(value, format);
+        }
+        catch (Exception e)
+        {
+            return $$"""{ Failed retrieving: {{e.Message}} }""";
+        }
+    }
+
+    private static string FormatObject<T>(T? value, string? format = null)
+    {
+        return value switch
+        {
+            null => "null",
+            IFormattable formattable => formattable.ToString(format, CultureInfo.InvariantCulture),
+            LocalizedString localizedS => FormatLocalizedString(localizedS),
+            string s => $"\"{s}\"",
+            _ => value.ToString()
+        };
+
+        string FormatLocalizedString(LocalizedString localizedS)
+        {
+            return localizedS.Parameters != null
+                ? $"{localizedS.Key} @({string.Join(", ", localizedS.Parameters.Select(x => FormatObject(x, format)))})" 
+                : localizedS.Key;
+        }
     }
 
     public static CrashReport Parse(string path)
@@ -90,38 +267,66 @@ internal class CrashReport : IDisposable
 
     public int GetDocumentCount() => ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")).Count();
 
-    public List<(string? originalPath, byte[] dotPixiBytes)> RecoverDocuments()
+    public bool TryRecoverDocuments(out List<RecoveredPixi> list)
     {
-        // Load .pixi files
-        Dictionary<string, byte[]> recoveredDocuments = new();
-        foreach (ZipArchiveEntry entry in ZipFile.Entries.Where(x => x.FullName.EndsWith(".pixi")))
+        try
         {
-            using Stream stream = entry.Open();
-            using MemoryStream memStream = new();
-            stream.CopyTo(memStream);
-            recoveredDocuments.Add(entry.Name, memStream.ToArray());
+            list = RecoverDocuments();
+        }
+        catch (Exception e)
+        {
+            list = null;
+            CrashHelper.SendExceptionInfoToWebhook(e);
+            return false;
         }
 
-        ZipArchiveEntry? originalPathsEntry = ZipFile.Entries.Where(entry => entry.FullName == "DocumentInfo.json").FirstOrDefault();
-        if (originalPathsEntry is null)
-            return recoveredDocuments.Select<KeyValuePair<string, byte[]>, (string?, byte[])>(keyValue => (null, keyValue.Value)).ToList();
+        return true;
+    }
+    
+    public List<RecoveredPixi> RecoverDocuments()
+    {
+        List<RecoveredPixi> recoveredDocuments = new();
 
-        // Load original paths
-        Dictionary<string, string?> originalPaths;
+        var paths = TryGetOriginalPaths();
+        if (paths == null)
         {
-            using Stream stream = originalPathsEntry.Open();
-            using StreamReader reader = new(stream);
-            string json = reader.ReadToEnd();
-            originalPaths = JsonConvert.DeserializeObject<Dictionary<string, string?>>(json);
+            recoveredDocuments.AddRange(
+                ZipFile.Entries
+                    .Where(x => 
+                        x.FullName.StartsWith("Documents") && 
+                        x.FullName.EndsWith(".pixi"))
+                    .Select(entry => new RecoveredPixi(null, entry)));
+
+            return recoveredDocuments;
         }
 
-        return (
-            from docKeyValue in recoveredDocuments
-            join pathKeyValue in originalPaths on docKeyValue.Key equals pathKeyValue.Key
-            select (pathKeyValue.Value, docKeyValue.Value)
-            ).ToList();
+        recoveredDocuments.AddRange(paths.Select(path => new RecoveredPixi(path.Value, ZipFile.GetEntry($"Documents/{path.Key}"))));
+
+        return recoveredDocuments;
+
+        Dictionary<string, string>? TryGetOriginalPaths()
+        {
+            var originalPathsEntry = ZipFile.Entries.FirstOrDefault(entry => entry.FullName == "DocumentInfo.json");
+
+            if (originalPathsEntry == null)
+                return null;
+            
+            try
+            {
+                using var stream = originalPathsEntry.Open();
+                using var reader = new StreamReader(stream);
+                string json = reader.ReadToEnd();
+                
+                return JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
+            }
+            catch
+            {
+                return null;
+            }
+        }
     }
 
+
     public void Dispose()
     {
         ZipFile.Dispose();
@@ -169,13 +374,15 @@ internal class CrashReport : IDisposable
 
         // Write the documents into zip
         int counter = 0;
-        Dictionary<string, string?> originalPaths = new();
-        foreach (DocumentViewModel document in vm.DocumentManagerSubViewModel.Documents)
+        var originalPaths = new Dictionary<string, string>();
+        foreach (var document in vm.DocumentManagerSubViewModel.Documents)
         {
             try
             {
                 string fileName = string.IsNullOrWhiteSpace(document.FullFilePath) ? "Unsaved" : Path.GetFileNameWithoutExtension(document.FullFilePath);
-                string nameInZip = $"{fileName}-{document.OpenedUTC}-{counter}.pixi".Replace(':', '_');
+                string nameInZip = $"{fileName}-{document.OpenedUTC.ToString(CultureInfo.InvariantCulture)}-{counter.ToString(CultureInfo.InvariantCulture)}.pixi"
+                    .Replace(':', '_')
+                    .Replace('/', '_');
 
                 byte[] serialized = PixiParser.Serialize(document.ToSerializable());
 
@@ -209,10 +416,27 @@ internal class CrashReport : IDisposable
         ReportText = Encoding.UTF8.GetString(encodedReport);
     }
 
-    internal class CrashReportUserMessage
+
+    public class RecoveredPixi
     {
-        public string Message { get; set; }
+        public string? Path { get; }
+        
+        public ZipArchiveEntry RecoveredEntry { get; }
+        
+        public byte[] GetRecoveredBytes()
+        {
+            var buffer = new byte[RecoveredEntry.Length];
+            using var stream = RecoveredEntry.Open();
 
-        public string Mail { get; set; }
+            stream.ReadExactly(buffer);
+            
+            return buffer;
+        }
+        
+        public RecoveredPixi(string? path, ZipArchiveEntry recoveredEntry)
+        {
+            Path = path;
+            RecoveredEntry = recoveredEntry;
+        }
     }
 }

+ 5 - 1
src/PixiEditor/Models/Dialogs/OptionsDialog.cs

@@ -2,6 +2,7 @@
 using System.Windows.Controls;
 using System.Windows.Media;
 using PixiEditor.Extensions.Common.Localization;
+using PixiEditor.Extensions.UI;
 using PixiEditor.Models.Localization;
 using PixiEditor.Views.Dialogs;
 
@@ -51,9 +52,12 @@ internal class OptionsDialog<T> : CustomDialog, IEnumerable<T>
         set => _results.Add(name, value);
     }
 
-    public override bool ShowDialog()
+    public override bool ShowDialog() => ShowDialog(false);
+
+    public bool ShowDialog(bool topmost)
     {
         var popup = new OptionPopup(Title, Content, new(_results.Keys.Select(x => (object)x)));
+        popup.Topmost = topmost;
         var popupResult = popup.ShowDialog();
 
         Result = (T)popup.Result;

+ 3 - 1
src/PixiEditor/Models/DocumentModels/DocumentUpdater.cs

@@ -190,12 +190,14 @@ internal class DocumentUpdater
 
     private void ProcessSetSelectedMember(SetSelectedMember_PassthroughAction info)
     {
+        StructureMemberViewModel? member = doc.StructureHelper.Find(info.GuidValue);
+        if (member is null || member.Selection == StructureMemberSelectionType.Hard)
+            return;
         if (doc.SelectedStructureMember is { } oldMember)
         {
             oldMember.Selection = StructureMemberSelectionType.None;
             oldMember.RaisePropertyChanged(nameof(oldMember.Selection));
         }
-        StructureMemberViewModel? member = doc.StructureHelper.FindOrThrow(info.GuidValue);
         member.Selection = StructureMemberSelectionType.Hard;
         member.RaisePropertyChanged(nameof(member.Selection));
         doc.InternalSetSelectedMember(member);

+ 37 - 14
src/PixiEditor/Models/IO/Exporter.cs

@@ -92,26 +92,23 @@ internal class Exporter
 
         var typeFromPath = SupportedFilesHelper.ParseImageFormat(Path.GetExtension(pathWithExtension));
 
-        if (typeFromPath != FileType.Pixi)
+        if (typeFromPath == FileType.Pixi)
         {
-            var maybeBitmap = document.MaybeRenderWholeImage();
-            if (maybeBitmap.IsT0)
-                return SaveResult.ConcurrencyError;
-            var bitmap = maybeBitmap.AsT1;
+            return TrySaveAsPixi(document, pathWithExtension);
+        }
 
-            if (!encodersFactory.ContainsKey(typeFromPath))
-            {
-                return SaveResult.UnknownError;
-            }
+        var maybeBitmap = document.MaybeRenderWholeImage();
+        if (maybeBitmap.IsT0)
+            return SaveResult.ConcurrencyError;
+        var bitmap = maybeBitmap.AsT1;
 
-            return TrySaveAs(encodersFactory[typeFromPath](), pathWithExtension, bitmap, exportSize);
-        }
-        else
+        if (!encodersFactory.ContainsKey(typeFromPath))
         {
-            Parser.PixiParser.Serialize(document.ToSerializable(), pathWithExtension);
+            return SaveResult.UnknownError;
         }
 
-        return SaveResult.Success;
+        return TrySaveAs(encodersFactory[typeFromPath](), pathWithExtension, bitmap, exportSize);
+
     }
 
     static Dictionary<FileType, Func<BitmapEncoder>> encodersFactory = new Dictionary<FileType, Func<BitmapEncoder>>();
@@ -172,6 +169,10 @@ internal class Exporter
         {
             return SaveResult.SecurityError;
         }
+        catch (UnauthorizedAccessException e)
+        {
+            return SaveResult.SecurityError;
+        }
         catch (IOException)
         {
             return SaveResult.IoError;
@@ -182,4 +183,26 @@ internal class Exporter
         }
         return SaveResult.Success;
     }
+
+    private static SaveResult TrySaveAsPixi(DocumentViewModel document, string pathWithExtension)
+    {
+        try
+        {
+            Parser.PixiParser.Serialize(document.ToSerializable(), pathWithExtension);
+        }
+        catch (UnauthorizedAccessException e)
+        {
+            return SaveResult.SecurityError;
+        }
+        catch (IOException)
+        {
+            return SaveResult.IoError;
+        }
+        catch
+        {
+            return SaveResult.UnknownError;
+        }
+
+        return SaveResult.Success;
+    }
 }

+ 14 - 3
src/PixiEditor/Models/IO/Importer.cs

@@ -30,8 +30,19 @@ internal class Importer : NotifyableObject
     /// <returns>WriteableBitmap of imported image.</returns>
     public static Surface? ImportImage(string path, VecI size)
     {
-        if (!Path.Exists(path)) return null;
-        Surface original = Surface.Load(path);
+        if (!Path.Exists(path))
+            throw new MissingFileException();
+            
+        Surface original;
+        try
+        {
+            original = Surface.Load(path);
+        }
+        catch (Exception e) when (e is ArgumentException or FileNotFoundException)
+        {
+            throw new CorruptedFileException(e);
+        }
+            
         if (original.Size == size || size == VecI.NegativeOne)
         {
             return original;
@@ -95,7 +106,7 @@ internal class Importer : NotifyableObject
 
                 return doc;
             }
-            catch (InvalidFileException e)
+            catch (Exception e)
             {
                 throw new CorruptedFileException("FAILED_TO_OPEN_FILE", e);
             }

+ 2 - 2
src/PixiEditor/Models/Rendering/CanvasUpdater.cs

@@ -147,6 +147,8 @@ internal class CanvasUpdater
                 break;
             }
         }
+        
+        UpdateAffectedNonRerenderedChunks(chunksToRerender, chunkGatherer.MainImageArea);
 
         bool anythingToUpdate = false;
         foreach (var (_, chunks) in chunksToRerender)
@@ -156,8 +158,6 @@ internal class CanvasUpdater
         if (!anythingToUpdate)
             return new();
 
-        UpdateAffectedNonRerenderedChunks(chunksToRerender, chunkGatherer.MainImageArea);
-        
         List<IRenderInfo> infos = new();
         UpdateMainImage(chunksToRerender, updatingStoredChunks ? null : chunkGatherer.MainImageArea.GlobalArea.Value, infos);
         return infos;

+ 1 - 1
src/PixiEditor/Models/Rendering/MemberPreviewUpdater.cs

@@ -563,7 +563,7 @@ internal class MemberPreviewUpdater
                 continue;
 
             if (tightBounds is null)
-                tightBounds = lastMainPreviewTightBounds[guid];
+                tightBounds = lastMaskPreviewTightBounds[guid];
 
             var previewSize = StructureMemberViewModel.CalculatePreviewSize(tightBounds.Value.Size);
             float scaling = (float)previewSize.X / tightBounds.Value.Width;

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

@@ -50,5 +50,5 @@ using System.Windows;
 // 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("1.2.1.0")]
-[assembly: AssemblyFileVersion("1.2.1.0")]
+[assembly: AssemblyVersion("1.2.2.0")]
+[assembly: AssemblyFileVersion("1.2.2.0")]

+ 24 - 2
src/PixiEditor/ViewModels/CrashReportViewModel.cs

@@ -3,8 +3,11 @@ using System.IO;
 using System.Net.Http;
 using System.Text;
 using System.Windows;
+using System.Windows.Threading;
+using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Helpers;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
 using PixiEditor.Views;
 using PixiEditor.Views.Dialogs;
 
@@ -40,16 +43,35 @@ internal class CrashReportViewModel : ViewModelBase
         AttachDebuggerCommand = new(AttachDebugger);
 
         if (!IsDebugBuild)
-            _ = CrashHelper.SendReportTextToWebhook(report);
+            _ = CrashHelper.SendReportTextToWebhookAsync(report);
     }
 
     public void RecoverDocuments(object args)
     {
-        MainWindow window = MainWindow.CreateWithDocuments(CrashReport.RecoverDocuments());
+        MainWindow window = MainWindow.CreateWithRecoveredDocuments(CrashReport, out var showMissingFilesDialog);
 
         Application.Current.MainWindow = window;
         window.Show();
         hasRecoveredDocuments = false;
+
+        if (showMissingFilesDialog)
+        {
+            var dialog = new OptionsDialog<LocalizedString>(
+                "CRASH_NOT_ALL_DOCUMENTS_RECOVERED_TITLE",
+                new LocalizedString("CRASH_NOT_ALL_DOCUMENTS_RECOVERED"))
+            {
+                {
+                    "SEND", _ =>
+                    {
+                        var sendReportDialog = new SendCrashReportWindow(CrashReport);
+                        sendReportDialog.ShowDialog();
+                    }
+                },
+                "CLOSE"
+            };
+
+            dialog.ShowDialog(true);
+        }
     }
 
     [Conditional("DEBUG")]

+ 6 - 1
src/PixiEditor/ViewModels/SubViewModels/Document/TransformOverlays/DocumentTransformViewModel.cs

@@ -1,4 +1,6 @@
-using System.Windows.Input;
+using System.Diagnostics;
+using System.Text;
+using System.Windows.Input;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.Extensions.Common.Localization;
@@ -10,6 +12,7 @@ using PixiEditor.Views.UserControls.Overlays.TransformOverlay;
 
 namespace PixiEditor.ViewModels.SubViewModels.Document.TransformOverlays;
 #nullable enable
+[DebuggerDisplay("{ToString(),nq}")]
 internal class DocumentTransformViewModel : NotifyableObject
 {
     private DocumentViewModel document;
@@ -246,4 +249,6 @@ internal class DocumentTransformViewModel : NotifyableObject
                 break;
         }
     }
+
+    public override string ToString() => !TransformActive ? "Not active" : $"Transform Mode: {activeTransformMode}; Corner Freedom: {CornerFreedom}; Side Freedom: {SideFreedom}";
 }

+ 19 - 13
src/PixiEditor/ViewModels/SubViewModels/Main/FileViewModel.cs

@@ -102,8 +102,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
         {
             OpenFromPath(file);
         }
-        else if ((Owner.DocumentManagerSubViewModel.Documents.Count == 0
-                  || !args.Contains("--crash")) && !args.Contains("--openedInExisting"))
+        else if ((Owner.DocumentManagerSubViewModel.Documents.Count == 0 && !args.Contains("--crash")) && !args.Contains("--openedInExisting"))
         {
             if (IPreferences.Current.GetPreference("ShowStartupWindow", true))
             {
@@ -157,7 +156,7 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
                 continue;
             }
             
-            OpenFromPath(dataImage.name, false);
+            OpenRegularImage(dataImage.image, null);
         }
     }
 
@@ -345,18 +344,25 @@ internal class FileViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.File.Export", "EXPORT", "EXPORT_IMAGE", CanExecute = "PixiEditor.HasDocument", Key = Key.E, Modifiers = ModifierKeys.Control)]
     public void ExportFile()
     {
-        DocumentViewModel doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
-        if (doc is null)
-            return;
+        try
+        {
+            DocumentViewModel doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+            if (doc is null)
+                return;
 
-        ExportFileDialog info = new ExportFileDialog(doc.SizeBindable);
-        if (info.ShowDialog())
+            ExportFileDialog info = new ExportFileDialog(doc.SizeBindable);
+            if (info.ShowDialog())
+            {
+                SaveResult result = Exporter.TrySaveUsingDataFromDialog(doc, info.FilePath, info.ChosenFormat, out string finalPath, new(info.FileWidth, info.FileHeight));
+                if (result == SaveResult.Success)
+                    ProcessHelper.OpenInExplorer(finalPath);
+                else
+                    ShowSaveError((DialogSaveResult)result);
+            }
+        }
+        catch (RecoverableException e)
         {
-            SaveResult result = Exporter.TrySaveUsingDataFromDialog(doc, info.FilePath, info.ChosenFormat, out string finalPath, new(info.FileWidth, info.FileHeight));
-            if (result == SaveResult.Success)
-                ProcessHelper.OpenInExplorer(finalPath);
-            else
-                ShowSaveError((DialogSaveResult)result);
+            NoticeDialog.Show(e.DisplayMessage, "ERROR");
         }
     }
 

+ 2 - 2
src/PixiEditor/ViewModels/SubViewModels/Main/MiscViewModel.cs

@@ -20,7 +20,7 @@ internal class MiscViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.Links.OpenRepository", "https://github.com/PixiEditor/PixiEditor", "REPOSITORY", "OPEN_REPOSITORY", IconPath = "Globe.png")]
     [Command.Basic("PixiEditor.Links.OpenLicense", "https://github.com/PixiEditor/PixiEditor/blob/master/LICENSE", "LICENSE", "OPEN_LICENSE", IconPath = "Globe.png")]
     [Command.Basic("PixiEditor.Links.OpenOtherLicenses", "https://pixieditor.net/docs/Third-party-licenses", "THIRD_PARTY_LICENSES", "OPEN_THIRD_PARTY_LICENSES", IconPath = "Globe.png")]
-    public static async Task OpenHyperlink(string url)
+    public static void OpenHyperlink(string url)
     {
         try
         {
@@ -28,8 +28,8 @@ internal class MiscViewModel : SubViewModel<ViewModelMain>
         }
         catch (Exception e)
         {
+            CrashHelper.SendExceptionInfoToWebhook(e);
             NoticeDialog.Show(title: "Error", message: $"Couldn't open the address {url} in your default browser");
-            await CrashHelper.SendExceptionInfoToWebhook(e);
         }
     }
 }

+ 5 - 0
src/PixiEditor/ViewModels/SubViewModels/Main/UpdateViewModel.cs

@@ -220,6 +220,11 @@ internal class UpdateViewModel : SubViewModel<ViewModelMain>
             {
                 NoticeDialog.Show("COULD_NOT_CHECK_FOR_UPDATES", "UPDATE_CHECK_FAILED");
             }
+            catch (Exception e)
+            {
+                CrashHelper.SendExceptionInfoToWebhookAsync(e);
+                NoticeDialog.Show("COULD_NOT_CHECK_FOR_UPDATES", "UPDATE_CHECK_FAILED");
+            }
 
             AskToInstall();
         }

+ 19 - 0
src/PixiEditor/ViewModels/ViewModelMain.cs

@@ -1,4 +1,5 @@
 using System.ComponentModel;
+using System.Timers;
 using System.Windows;
 using Microsoft.Extensions.DependencyInjection;
 using PixiEditor.DrawingApi.Core.ColorsImpl;
@@ -14,9 +15,11 @@ using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Events;
 using PixiEditor.Models.Localization;
+using PixiEditor.Platform;
 using PixiEditor.ViewModels.SubViewModels.AdditionalContent;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Tools;
+using Timer = System.Timers.Timer;
 
 namespace PixiEditor.ViewModels;
 
@@ -81,6 +84,8 @@ internal class ViewModelMain : ViewModelBase
     public IPreferences Preferences { get; set; }
     public ILocalizationProvider LocalizationProvider { get; set; }
 
+    private Timer _updateTimer;
+
     public LocalizedString ActiveActionDisplay
     {
         get
@@ -161,6 +166,20 @@ internal class ViewModelMain : ViewModelBase
         ExtensionsSubViewModel = services.GetService<ExtensionsViewModel>(); // Must be last
 
         DocumentManagerSubViewModel.ActiveDocumentChanged += OnActiveDocumentChanged;
+        InitUpdateTimer();
+    }
+
+    private void InitUpdateTimer()
+    {
+        // Enable this while implementing something that needs it
+        /*_updateTimer = new Timer(50);
+        _updateTimer.Elapsed += UpdateTimerOnElapsed;
+        _updateTimer.Start();*/
+    }
+
+    private void UpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
+    {
+        IPlatform.Current?.Update();
     }
 
     public bool DocumentIsNotNull(object property)

+ 14 - 3
src/PixiEditor/Views/Dialogs/HelloTherePopup.xaml.cs

@@ -3,10 +3,12 @@ using System.Diagnostics;
 using System.IO;
 using System.Windows;
 using System.Windows.Input;
+using PixiEditor.Exceptions;
 using PixiEditor.Extensions.Common.UserPreferences;
 using PixiEditor.Helpers;
 using PixiEditor.Models.Commands;
 using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Services.NewsFeed;
 using PixiEditor.ViewModels.SubViewModels.Main;
 
@@ -193,8 +195,17 @@ internal partial class HelloTherePopup : Window
 
     private void OpenInExplorer(object parameter)
     {
-        if (parameter is not string value) return;
-        ProcessHelper.OpenInExplorer(value);
+        if (parameter is not string value) 
+            return;
+
+        try
+        {
+            ProcessHelper.OpenInExplorer(value);
+        }
+        catch (RecoverableException e)
+        {
+            NoticeDialog.Show(e.DisplayMessage, "INTERNAL_ERROR");
+        }
     }
 
     private bool CanOpenInExplorer(object parameter) => File.Exists((string)parameter);
@@ -227,7 +238,7 @@ internal partial class HelloTherePopup : Window
         {
             IsFetchingNews = false;
             FailedFetchingNews = true;
-            await CrashHelper.SendExceptionInfoToWebhook(ex);
+            await CrashHelper.SendExceptionInfoToWebhookAsync(ex);
         }
     }
 }

+ 1 - 0
src/PixiEditor/Views/Dialogs/OptionsPopup.xaml.cs

@@ -39,6 +39,7 @@ internal partial class OptionPopup : Window
 
     public OptionPopup(string title, object content, ObservableCollection<object> options)
     {
+        Title = title;
         PopupContent = content;
         Options = options;
         CancelCommand = new RelayCommand(Cancel);

+ 27 - 0
src/PixiEditor/Views/LoadingWindow.xaml

@@ -0,0 +1,27 @@
+<Window x:Class="PixiEditor.Views.LoadingWindow"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:local="clr-namespace:PixiEditor.Views"
+        xmlns:gif="http://wpfanimatedgif.codeplex.com"
+        mc:Ignorable="d" ShowInTaskbar="False" WindowStyle="None"
+        ResizeMode="NoResize" WindowStartupLocation="CenterScreen"
+        Title="LoadingWindow" Height="180" Width="160"
+        Background="Transparent"
+        AllowsTransparency="True"
+        x:Name="Window">
+    <Border Background="{StaticResource AccentColor}"
+            BorderBrush="{StaticResource MainColor}" BorderThickness="1.5"
+            CornerRadius="10">
+        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
+            <Image
+                gif:ImageBehavior.AnimatedSource="{Binding LoadingImage, ElementName=Window}"
+                Height="70"
+                gif:ImageBehavior.AnimationSpeedRatio="1.5"/>
+            <TextBlock Foreground="White" Text="PixiEditor"
+                       FontFamily="Roboto" FontWeight="900" FontSize="28"
+                       Margin="0,10,0,0"/>
+        </StackPanel>
+    </Border>
+</Window>

+ 43 - 0
src/PixiEditor/Views/LoadingWindow.xaml.cs

@@ -0,0 +1,43 @@
+using System.Windows;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Threading;
+
+namespace PixiEditor.Views;
+
+public partial class LoadingWindow : Window
+{
+    public static LoadingWindow Instance { get; private set; }
+
+    public ImageSource LoadingImage { get; } = new BitmapImage(new Uri("pack://application:,,,/images/processing.gif"));
+    
+    public LoadingWindow()
+    {
+        InitializeComponent();
+    }
+
+    public static void ShowInNewThread()
+    {
+        var thread = new Thread(ThreadStart) { IsBackground = true };
+
+        thread.SetApartmentState(ApartmentState.STA);
+        thread.Start();
+    }
+
+    public void SafeClose()
+    {
+        Dispatcher.Invoke(Close);
+    }
+
+    private static void ThreadStart()
+    {
+        Instance = new LoadingWindow();
+        Instance.Show();
+
+        Instance.Closed += (_, _) =>
+            Instance.Dispatcher.InvokeShutdown();
+
+        Dispatcher.Run();
+    }
+}
+

+ 0 - 17
src/PixiEditor/Views/MainWindow.xaml

@@ -101,23 +101,6 @@
         </b:EventTrigger>
     </b:Interaction.Triggers>
     <Grid>
-        <Border HorizontalAlignment="Right" VerticalAlignment="Bottom" Name="steamRefresher"
-                Visibility="Collapsed"
-                Background="Transparent"
-                BorderThickness="0"
-                Focusable="False"
-                Width="{Binding ElementName=mainWindow, Path=Width}"
-                Height="{Binding ElementName=mainWindow, Path=Height}" Panel.ZIndex="-1000">
-            <Border.Triggers>
-                <EventTrigger RoutedEvent="Loaded">
-                    <BeginStoryboard>
-                        <Storyboard>
-                            <DoubleAnimation Storyboard.TargetProperty="Opacity" RepeatBehavior="Forever" From="0" To="1" Duration="0:0:10"/>
-                        </Storyboard>
-                    </BeginStoryboard>
-                </EventTrigger>
-            </Border.Triggers>
-        </Border>
         <Grid
             Name="mainGrid"
             Margin="5"

+ 47 - 20
src/PixiEditor/Views/MainWindow.xaml.cs

@@ -4,6 +4,7 @@ using System.Windows;
 using System.Windows.Controls;
 using System.Windows.Input;
 using System.Windows.Interop;
+using System.Windows.Media.Animation;
 using System.Windows.Media.Imaging;
 using AvalonDock.Layout;
 using Microsoft.Extensions.DependencyInjection;
@@ -15,11 +16,14 @@ using PixiEditor.Extensions.UI;
 using PixiEditor.Helpers;
 using PixiEditor.Models.AppExtensions;
 using PixiEditor.Models.Controllers;
+using PixiEditor.Models.DataHolders;
+using PixiEditor.Models.Dialogs;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.IO;
 using PixiEditor.Platform;
 using PixiEditor.ViewModels.SubViewModels.Document;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
+using PixiEditor.Views.Dialogs;
 
 namespace PixiEditor.Views;
 
@@ -56,14 +60,12 @@ internal partial class MainWindow : Window
 
         preferences = services.GetRequiredService<IPreferences>();
         platform = services.GetRequiredService<IPlatform>();
-        DataContext = services.GetRequiredService<ViewModelMain>();
 
+        DataContext = services.GetRequiredService<ViewModelMain>();
         DataContext.Setup(services);
 
         InitializeComponent();
 
-        StartSteamRefresher();
-
         OnDataContextInitialized?.Invoke();
         pixiEditorLogo = BitmapFactory.FromResource(@"/Images/PixiEditorLogo.png");
 
@@ -80,21 +82,14 @@ internal partial class MainWindow : Window
         });
 
         DataContext.DocumentManagerSubViewModel.ActiveDocumentChanged += DocumentChanged;
-
-        StartSteamRefresher();
+        
+        ContentRendered += OnContentRendered;
     }
 
-    private void StartSteamRefresher()
+    private void OnContentRendered(object sender, EventArgs e)
     {
-#if STEAM
-        steamRefresher.Visibility = Visibility.Visible;
-
-        PixiEditor.Platform.Steam.SteamOverlayHandler handler = new PixiEditor.Platform.Steam.SteamOverlayHandler();
-        handler.ActivateRefreshingElement += (bool activate) =>
-        {
-            steamRefresher.Visibility = activate ? Visibility.Visible : Visibility.Collapsed;
-        };
-#endif
+        LoadingWindow.Instance?.SafeClose();
+        Activate();
     }
 
     private void SetupTranslator()
@@ -112,17 +107,49 @@ internal partial class MainWindow : Window
         GlobalMouseHook.Instance.Initilize(this);
     }
 
-    public static MainWindow CreateWithDocuments(IEnumerable<(string? originalPath, byte[] dotPixiBytes)> documents)
+    public static MainWindow CreateWithRecoveredDocuments(CrashReport report, out bool showMissingFilesDialog)
     {
-        MainWindow window = new(extLoader);
-        FileViewModel fileVM = window.services.GetRequiredService<FileViewModel>();
+        var window = GetMainWindow();
+        var fileVM = window.services.GetRequiredService<FileViewModel>();
+
+        if (!report.TryRecoverDocuments(out var documents))
+        {
+            showMissingFilesDialog = true;
+            return window;
+        }
+
+        var i = 0;
 
-        foreach (var (path, bytes) in documents)
+        foreach (var document in documents)
         {
-            fileVM.OpenRecoveredDotPixi(path, bytes);
+            try
+            {
+                fileVM.OpenRecoveredDotPixi(document.Path, document.GetRecoveredBytes());
+                i++;
+            }
+            catch (Exception e)
+            {
+                CrashHelper.SendExceptionInfoToWebhook(e);
+            }
         }
 
+        showMissingFilesDialog = documents.Count != i;
+
         return window;
+
+        MainWindow GetMainWindow()
+        {
+            try
+            {
+                var app = (App)Application.Current;
+                return new MainWindow(app.InitApp());
+            }
+            catch (Exception e)
+            {
+                CrashHelper.SendExceptionInfoToWebhook(e, true);
+                throw;
+            }
+        }
     }
 
     /// <summary>Brings main window to foreground.</summary>

+ 3 - 3
src/PixiEditor/Views/UserControls/Overlays/BrushShapeOverlay/BrushShapeOverlay.cs

@@ -23,7 +23,7 @@ internal class BrushShapeOverlay : Control
             new FrameworkPropertyMetadata(1, FrameworkPropertyMetadataOptions.AffectsRender));
 
     public static readonly DependencyProperty MouseEventSourceProperty =
-        DependencyProperty.Register(nameof(MouseEventSource), typeof(UIElement), typeof(BrushShapeOverlay), new(null));
+        DependencyProperty.Register(nameof(MouseEventSource), typeof(FrameworkElement), typeof(BrushShapeOverlay), new(null));
 
     public static readonly DependencyProperty MouseReferenceProperty =
         DependencyProperty.Register(nameof(MouseReference), typeof(UIElement), typeof(BrushShapeOverlay), new(null));
@@ -44,9 +44,9 @@ internal class BrushShapeOverlay : Control
         set => SetValue(MouseReferenceProperty, value);
     }
 
-    public UIElement? MouseEventSource
+    public FrameworkElement? MouseEventSource
     {
-        get => (UIElement?)GetValue(MouseEventSourceProperty);
+        get => (FrameworkElement?)GetValue(MouseEventSourceProperty);
         set => SetValue(MouseEventSourceProperty, value);
     }
 

+ 17 - 0
src/PixiEditor/Views/UserControls/SteamOverlay.xaml

@@ -0,0 +1,17 @@
+<UserControl x:Class="PixiEditor.Views.UserControls.SteamOverlay"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+             xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
+             Name="uc"
+             mc:Ignorable="d">
+    <Grid
+          Width="{Binding Width, ElementName=uc}"
+            Height="{Binding Height, ElementName=uc}"
+          Background="Transparent"
+            Focusable="False"
+            Panel.ZIndex="-1000"
+            x:Name="refresher">
+    </Grid>
+</UserControl>

+ 77 - 0
src/PixiEditor/Views/UserControls/SteamOverlay.xaml.cs

@@ -0,0 +1,77 @@
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Threading;
+
+namespace PixiEditor.Views.UserControls;
+
+public partial class SteamOverlay : UserControl
+{
+    private DispatcherTimer _timer;
+    private DispatcherTimer _fadeTimer;
+    public SteamOverlay()
+    {
+        InitializeComponent();
+        CreateRefreshTimer();
+    }
+
+    private void CreateFadeTimer()
+    {
+        StopFadeTimer();
+        _fadeTimer = new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromSeconds(1f) };
+        _fadeTimer.Tick += FadeOut;
+    }
+
+    private void FadeOut(object sender, EventArgs eventArgs)
+    {
+        RemoveTimer();
+        Visibility = Visibility.Collapsed;
+        StopFadeTimer();
+    }
+
+    private void CreateRefreshTimer()
+    {
+        RemoveTimer();
+        _timer = new DispatcherTimer(DispatcherPriority.Render) { Interval = TimeSpan.FromMilliseconds(16.6f) };
+        _timer.Tick += Refresh;
+    }
+
+    private void RemoveTimer()
+    {
+        if (_timer != null)
+        {
+            _timer.Stop();
+            _timer.Tick -= Refresh;
+            _timer = null;
+        }
+    }
+
+    public void Activate()
+    {
+        StopFadeTimer();
+        CreateRefreshTimer();
+        Visibility = Visibility.Visible;
+        _timer.Start();
+    }
+
+    public void Deactivate()
+    {
+        CreateFadeTimer();
+        _fadeTimer.Start();
+    }
+
+    private void StopFadeTimer()
+    {
+        if (_fadeTimer != null)
+        {
+            _fadeTimer.Stop();
+            _fadeTimer.Tick -= FadeOut;
+            _fadeTimer = null;
+        }
+    }
+
+    private void Refresh(object sender, EventArgs e)
+    {
+        refresher.InvalidateVisual();
+    }
+}
+