diff --git a/.editorconfig b/.editorconfig
index c04a279..2cd5f5b 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,7 +9,9 @@ charset = utf-8
indent_style = space
indent_size = 4
tab_width = 4
-end_of_line = crlf
+# LF on every platform, matching the `* text=auto eol=lf` default in .gitattributes.
+# Overrides below must stay in step with the eol pins in that file.
+end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
@@ -17,7 +19,7 @@ trim_trailing_whitespace = true
[*.{cs,csx,cake,fs,fsx,vb,vbx}]
indent_style = tab
-file_header_template = Copyright (c) ktsu.dev\nAll rights reserved.\nLicensed under the MIT license.
+file_header_template = Copyright (c) 2023-2026 ktsu-dev contributors
# Default severity for all .NET Code Style rules
dotnet_analyzer_diagnostic.severity = error
@@ -503,4 +505,9 @@ indent_style = tab
# Shell scripts
[*.sh]
end_of_line = lf
-indent_size = 2
\ No newline at end of file
+indent_size = 2
+
+# Windows batch scripts and Visual Studio solution files keep CRLF on every platform.
+# These match the eol=crlf pins in .gitattributes.
+[*.{cmd,bat,sln}]
+end_of_line = crlf
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
index d67e40c..a0bea35 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -4,20 +4,25 @@
# Git Line Endings #
###############################
-# Set default behaviour to automatically normalize line endings.
-* text=auto
+# Normalize all text files to LF in the repository, and check them out as LF on every
+# platform. The explicit eol overrides each machine's core.autocrlf, so the working tree
+# is byte-identical on Windows, Linux and macOS. This must stay in step with the
+# end_of_line settings in .editorconfig.
+* text=auto eol=lf
-# csharp files to match .editorconfig
-*.cs text eol=crlf
+# Force bash scripts to always use LF line endings so that if a repo is accessed
+# in Unix via a file share from Windows, the scripts will work. Redundant with the
+# default above, kept explicit because these files break outright with CRLF.
+*.sh text eol=lf
# Force batch scripts to always use CRLF line endings so that if a repo is accessed
# in Windows via a file share from Linux, the scripts will work.
-*.{cmd,[cC][mM][dD]} text eol=crlf
-*.{bat,[bB][aA][tT]} text eol=crlf
+*.cmd text eol=crlf
+*.bat text eol=crlf
-# Force bash scripts to always use LF line endings so that if a repo is accessed
-# in Unix via a file share from Windows, the scripts will work.
-*.sh text eol=lf
+# Visual Studio rewrites solution files with CRLF regardless of the checkout, so pin
+# them to avoid a spurious whole-file diff every time the solution is opened.
+*.sln text eol=crlf
###############################
# Git Large File System (LFS) #
diff --git a/.runsettings b/.runsettings
index 3c3169d..9bd9f05 100644
--- a/.runsettings
+++ b/.runsettings
@@ -1,25 +1,6 @@
-
- .\coverage
+ TestResults
-
-
- .\coverage
-
-
-
-
-
-
-
- opencover
- coverage.opencover.xml
- [*Test*]*,[*Tests*]*
- **/obj/**/*
-
-
-
-
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9697e4e..418c649 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,13 +5,11 @@
-
-
diff --git a/UndoRedo.Core/BaseCommand.cs b/UndoRedo.Core/BaseCommand.cs
index bd740e3..4230dd3 100644
--- a/UndoRedo.Core/BaseCommand.cs
+++ b/UndoRedo.Core/BaseCommand.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/CompositeCommand.cs b/UndoRedo.Core/CompositeCommand.cs
index bfd3e73..5f66bf7 100644
--- a/UndoRedo.Core/CompositeCommand.cs
+++ b/UndoRedo.Core/CompositeCommand.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Contracts/ICommand.cs b/UndoRedo.Core/Contracts/ICommand.cs
index 60919b7..ccef6cf 100644
--- a/UndoRedo.Core/Contracts/ICommand.cs
+++ b/UndoRedo.Core/Contracts/ICommand.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Contracts/ICommandMerger.cs b/UndoRedo.Core/Contracts/ICommandMerger.cs
index 204906b..4e1219e 100644
--- a/UndoRedo.Core/Contracts/ICommandMerger.cs
+++ b/UndoRedo.Core/Contracts/ICommandMerger.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Contracts;
diff --git a/UndoRedo.Core/Contracts/INavigationProvider.cs b/UndoRedo.Core/Contracts/INavigationProvider.cs
index e0f7787..62f56ab 100644
--- a/UndoRedo.Core/Contracts/INavigationProvider.cs
+++ b/UndoRedo.Core/Contracts/INavigationProvider.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Contracts/ISaveBoundaryManager.cs b/UndoRedo.Core/Contracts/ISaveBoundaryManager.cs
index 3a31976..07bd66a 100644
--- a/UndoRedo.Core/Contracts/ISaveBoundaryManager.cs
+++ b/UndoRedo.Core/Contracts/ISaveBoundaryManager.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Contracts;
diff --git a/UndoRedo.Core/Contracts/IStackManager.cs b/UndoRedo.Core/Contracts/IStackManager.cs
index 3262b0a..a951655 100644
--- a/UndoRedo.Core/Contracts/IStackManager.cs
+++ b/UndoRedo.Core/Contracts/IStackManager.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Contracts;
diff --git a/UndoRedo.Core/Contracts/IUndoRedoSerializer.cs b/UndoRedo.Core/Contracts/IUndoRedoSerializer.cs
index 14f75c2..78c6846 100644
--- a/UndoRedo.Core/Contracts/IUndoRedoSerializer.cs
+++ b/UndoRedo.Core/Contracts/IUndoRedoSerializer.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Contracts;
diff --git a/UndoRedo.Core/Contracts/IUndoRedoService.cs b/UndoRedo.Core/Contracts/IUndoRedoService.cs
index 11968ed..e9c96e5 100644
--- a/UndoRedo.Core/Contracts/IUndoRedoService.cs
+++ b/UndoRedo.Core/Contracts/IUndoRedoService.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Contracts;
diff --git a/UndoRedo.Core/DelegateCommand.cs b/UndoRedo.Core/DelegateCommand.cs
index d00126e..7bdc6d3 100644
--- a/UndoRedo.Core/DelegateCommand.cs
+++ b/UndoRedo.Core/DelegateCommand.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Models/ChangeMetadata.cs b/UndoRedo.Core/Models/ChangeMetadata.cs
index 9987e16..0d9332e 100644
--- a/UndoRedo.Core/Models/ChangeMetadata.cs
+++ b/UndoRedo.Core/Models/ChangeMetadata.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Models/ChangeVisualization.cs b/UndoRedo.Core/Models/ChangeVisualization.cs
index a6dc1d9..74a4300 100644
--- a/UndoRedo.Core/Models/ChangeVisualization.cs
+++ b/UndoRedo.Core/Models/ChangeVisualization.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Models;
diff --git a/UndoRedo.Core/Models/SaveBoundary.cs b/UndoRedo.Core/Models/SaveBoundary.cs
index d6b563a..992e086 100644
--- a/UndoRedo.Core/Models/SaveBoundary.cs
+++ b/UndoRedo.Core/Models/SaveBoundary.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Models/UndoRedoEventArgs.cs b/UndoRedo.Core/Models/UndoRedoEventArgs.cs
index 82bab40..00f270b 100644
--- a/UndoRedo.Core/Models/UndoRedoEventArgs.cs
+++ b/UndoRedo.Core/Models/UndoRedoEventArgs.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo;
diff --git a/UndoRedo.Core/Models/UndoRedoOptions.cs b/UndoRedo.Core/Models/UndoRedoOptions.cs
index 1f98db9..4d18b33 100644
--- a/UndoRedo.Core/Models/UndoRedoOptions.cs
+++ b/UndoRedo.Core/Models/UndoRedoOptions.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Models;
diff --git a/UndoRedo.Core/Models/UndoRedoStackState.cs b/UndoRedo.Core/Models/UndoRedoStackState.cs
index 29b7ed7..175d2fd 100644
--- a/UndoRedo.Core/Models/UndoRedoStackState.cs
+++ b/UndoRedo.Core/Models/UndoRedoStackState.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Models;
diff --git a/UndoRedo.Core/ServiceCollectionExtensions.cs b/UndoRedo.Core/ServiceCollectionExtensions.cs
index 9e42c85..203c39d 100644
--- a/UndoRedo.Core/ServiceCollectionExtensions.cs
+++ b/UndoRedo.Core/ServiceCollectionExtensions.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Core;
diff --git a/UndoRedo.Core/Services/CommandMerger.cs b/UndoRedo.Core/Services/CommandMerger.cs
index 5fa79d4..e5d2371 100644
--- a/UndoRedo.Core/Services/CommandMerger.cs
+++ b/UndoRedo.Core/Services/CommandMerger.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Core.Services;
diff --git a/UndoRedo.Core/Services/JsonUndoRedoSerializer.cs b/UndoRedo.Core/Services/JsonUndoRedoSerializer.cs
index 3464074..0f408f8 100644
--- a/UndoRedo.Core/Services/JsonUndoRedoSerializer.cs
+++ b/UndoRedo.Core/Services/JsonUndoRedoSerializer.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Core.Services;
diff --git a/UndoRedo.Core/Services/SaveBoundaryManager.cs b/UndoRedo.Core/Services/SaveBoundaryManager.cs
index 4801c5b..b6a1ebb 100644
--- a/UndoRedo.Core/Services/SaveBoundaryManager.cs
+++ b/UndoRedo.Core/Services/SaveBoundaryManager.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Core.Services;
diff --git a/UndoRedo.Core/Services/StackManager.cs b/UndoRedo.Core/Services/StackManager.cs
index f85ff91..59d9056 100644
--- a/UndoRedo.Core/Services/StackManager.cs
+++ b/UndoRedo.Core/Services/StackManager.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Core.Services;
diff --git a/UndoRedo.Core/Services/UndoRedoService.cs b/UndoRedo.Core/Services/UndoRedoService.cs
index 452096d..d47a82c 100644
--- a/UndoRedo.Core/Services/UndoRedoService.cs
+++ b/UndoRedo.Core/Services/UndoRedoService.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Core.Services;
diff --git a/UndoRedo.Test/CompositeCommandTests.cs b/UndoRedo.Test/CompositeCommandTests.cs
index 472bebc..3223990 100644
--- a/UndoRedo.Test/CompositeCommandTests.cs
+++ b/UndoRedo.Test/CompositeCommandTests.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Test;
diff --git a/UndoRedo.Test/SerializationTests.cs b/UndoRedo.Test/SerializationTests.cs
index fb20f18..f07d4d7 100644
--- a/UndoRedo.Test/SerializationTests.cs
+++ b/UndoRedo.Test/SerializationTests.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
namespace ktsu.UndoRedo.Test;
diff --git a/UndoRedo.Test/UndoRedoStackTests.cs b/UndoRedo.Test/UndoRedoStackTests.cs
index 06f79b7..015f040 100644
--- a/UndoRedo.Test/UndoRedoStackTests.cs
+++ b/UndoRedo.Test/UndoRedoStackTests.cs
@@ -1,6 +1,4 @@
-// Copyright (c) ktsu.dev
-// All rights reserved.
-// Licensed under the MIT license.
+// Copyright (c) 2023-2026 ktsu-dev contributors
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
diff --git a/global.json b/global.json
index 15c86b6..39f7683 100644
--- a/global.json
+++ b/global.json
@@ -5,15 +5,15 @@
},
"msbuild-sdks": {
"MSTest.Sdk": "4.3.3",
- "ktsu.Sdk": "2.18.0",
- "ktsu.Sdk.ConsoleApp": "2.18.0",
- "ktsu.Sdk.Tool": "2.18.0",
- "ktsu.Sdk.App": "2.18.0",
- "ktsu.Sdk.Windows": "2.18.0",
- "ktsu.Sdk.Linux": "2.18.0",
- "ktsu.Sdk.macOS": "2.18.0",
- "ktsu.Sdk.iOS": "2.18.0",
- "ktsu.Sdk.Android": "2.18.0"
+ "ktsu.Sdk": "2.21.1",
+ "ktsu.Sdk.ConsoleApp": "2.21.1",
+ "ktsu.Sdk.Tool": "2.21.1",
+ "ktsu.Sdk.App": "2.21.1",
+ "ktsu.Sdk.Windows": "2.21.1",
+ "ktsu.Sdk.Linux": "2.21.1",
+ "ktsu.Sdk.macOS": "2.21.1",
+ "ktsu.Sdk.iOS": "2.21.1",
+ "ktsu.Sdk.Android": "2.21.1"
},
"test": {
"runner": "Microsoft.Testing.Platform"