diff --git a/README.md b/README.md index 296d79d..b63c70d 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ SharpFM supports plugins via the `SharpFM.Plugin` contract library. Plugins are ### Writing a Plugin -1. Create a .NET 8 class library referencing `SharpFM.Plugin`. +1. Create a .NET 10 class library referencing `SharpFM.Plugin`. 2. Implement `IPanelPlugin` -- provide an `Id`, `DisplayName`, `CreatePanel()` returning an Avalonia `Control`. 3. Use `IPluginHost` in `Initialize()` to observe clip selection changes and content updates. 4. Optionally register keyboard shortcuts via `KeyBindings` and custom menu actions via `MenuActions`. @@ -94,10 +94,6 @@ SharpFM supports plugins via the `SharpFM.Plugin` contract library. Plugins are See `src/SharpFM.Plugin.Sample/` for a complete working example. -### Plugin License - -While SharpFM is licensed under GPL v3, plugins that communicate solely through the interfaces in `SharpFM.Plugin` are not required to be GPL-licensed. See the plugin interface source files for the full exception clause. - ## Troubleshooting Logs are stored in `${specialfolder:folder=CommonApplicationData}\SharpFM` and are automatically rotated after thirty days. diff --git a/docs/plugins/overview.md b/docs/plugins/overview.md index cc0a8d5..f95f6e9 100644 --- a/docs/plugins/overview.md +++ b/docs/plugins/overview.md @@ -15,12 +15,12 @@ SharpFM supports four types of plugins, all sharing a common base interface (`IP ### 1. Create a Class Library -Create a new .NET 8 class library and reference `SharpFM.Plugin`: +Create a new .NET 10 class library and reference `SharpFM.Plugin`: ```xml - net8.0 + net10.0 @@ -48,4 +48,4 @@ SharpFM scans the `plugins/` directory at startup for `.dll` files. Each assembl ## Licensing -The `SharpFM.Plugin` project is GPL with a **plugin exception**: plugins that implement these interfaces are not subject to the GPL and may use any license, including proprietary. +SharpFM and its plugin interfaces are licensed under the GNU General Public License v3. diff --git a/src/SharpFM.Plugin/ClipContentChangedArgs.cs b/src/SharpFM.Plugin/ClipContentChangedArgs.cs index c8ce92d..18f4f66 100644 --- a/src/SharpFM.Plugin/ClipContentChangedArgs.cs +++ b/src/SharpFM.Plugin/ClipContentChangedArgs.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using SharpFM.Model; namespace SharpFM.Plugin; diff --git a/src/SharpFM.Plugin/IClipTransformPlugin.cs b/src/SharpFM.Plugin/IClipTransformPlugin.cs index bcd1b84..56a5202 100644 --- a/src/SharpFM.Plugin/IClipTransformPlugin.cs +++ b/src/SharpFM.Plugin/IClipTransformPlugin.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using System.Threading.Tasks; namespace SharpFM.Plugin; diff --git a/src/SharpFM.Plugin/IEventPlugin.cs b/src/SharpFM.Plugin/IEventPlugin.cs index f88f9cd..c81d464 100644 --- a/src/SharpFM.Plugin/IEventPlugin.cs +++ b/src/SharpFM.Plugin/IEventPlugin.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - namespace SharpFM.Plugin; /// diff --git a/src/SharpFM.Plugin/IPanelPlugin.cs b/src/SharpFM.Plugin/IPanelPlugin.cs index e585389..6135cad 100644 --- a/src/SharpFM.Plugin/IPanelPlugin.cs +++ b/src/SharpFM.Plugin/IPanelPlugin.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using Avalonia.Controls; namespace SharpFM.Plugin; diff --git a/src/SharpFM.Plugin/IPersistencePlugin.cs b/src/SharpFM.Plugin/IPersistencePlugin.cs index fccee30..49c59d6 100644 --- a/src/SharpFM.Plugin/IPersistencePlugin.cs +++ b/src/SharpFM.Plugin/IPersistencePlugin.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using SharpFM.Model; namespace SharpFM.Plugin; diff --git a/src/SharpFM.Plugin/IPlugin.cs b/src/SharpFM.Plugin/IPlugin.cs index e7aad38..2752ec2 100644 --- a/src/SharpFM.Plugin/IPlugin.cs +++ b/src/SharpFM.Plugin/IPlugin.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using System; using System.Collections.Generic; diff --git a/src/SharpFM.Plugin/IPluginHost.cs b/src/SharpFM.Plugin/IPluginHost.cs index fd8312f..72f54f1 100644 --- a/src/SharpFM.Plugin/IPluginHost.cs +++ b/src/SharpFM.Plugin/IPluginHost.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; diff --git a/src/SharpFM.Plugin/PluginKeyBinding.cs b/src/SharpFM.Plugin/PluginKeyBinding.cs index e659236..9e1baf0 100644 --- a/src/SharpFM.Plugin/PluginKeyBinding.cs +++ b/src/SharpFM.Plugin/PluginKeyBinding.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using System; namespace SharpFM.Plugin; diff --git a/src/SharpFM.Plugin/PluginMenuAction.cs b/src/SharpFM.Plugin/PluginMenuAction.cs index 6b910aa..7bd6478 100644 --- a/src/SharpFM.Plugin/PluginMenuAction.cs +++ b/src/SharpFM.Plugin/PluginMenuAction.cs @@ -1,8 +1,3 @@ -// This file is part of SharpFM and is licensed under the GNU General Public License v3. -// -// Plugin Exception: You may create plugins that implement these interfaces without those -// plugins being subject to the GPL. Such plugins may use any license, including proprietary. - using System; namespace SharpFM.Plugin; diff --git a/src/SharpFM/Editors/ScriptClipEditor.cs b/src/SharpFM/Editors/ScriptClipEditor.cs index b39c98c..ee8e8aa 100644 --- a/src/SharpFM/Editors/ScriptClipEditor.cs +++ b/src/SharpFM/Editors/ScriptClipEditor.cs @@ -101,8 +101,11 @@ internal bool UpdateSealedXml(TextAnchor anchor, XElement newXml) if (!SignatureMatches(anchor, entry.Signature)) return false; var line = Document.GetLineByOffset(anchor.Offset); + var currentText = Document.GetText(line.Offset, line.Length); + var indent = currentText[..^currentText.AsSpan().TrimStart().Length]; + var newStep = ScriptStep.FromXml(newXml); - var newDisplay = newStep.ToDisplayLine(); + var newDisplay = indent + newStep.ToDisplayLine(); Document.Replace(line.Offset, line.Length, newDisplay); diff --git a/tests/SharpFM.Tests/Scripting/SealedStepPreservationTests.cs b/tests/SharpFM.Tests/Scripting/SealedStepPreservationTests.cs index 5cc4db7..f7a84c3 100644 --- a/tests/SharpFM.Tests/Scripting/SealedStepPreservationTests.cs +++ b/tests/SharpFM.Tests/Scripting/SealedStepPreservationTests.cs @@ -137,6 +137,26 @@ public void UpdateSealedXml_ReplacesCachedXmlAndRerendersLine() Assert.Equal("42", beep.Element("SomeChild")!.Attribute("value")!.Value); } + [Fact] + public void UpdateSealedXml_PreservesBlockIndentation() + { + // The Beep step is inside an If/End If block, so its display line + // is indented. Updating via the cog must preserve that indentation. + var editor = new ScriptClipEditor(ScriptWithSealedBeepXml); + + var anchor = editor.SealedAnchors.First(); + var beepLine = editor.Document.GetLineByOffset(anchor.Offset); + var beforeText = editor.Document.GetText(beepLine.Offset, beepLine.Length); + Assert.StartsWith(" ", beforeText); // 4-space indent inside If block + + var replacement = XElement.Parse( + ""); + editor.UpdateSealedXml(anchor, replacement); + + var afterText = editor.Document.GetText(beepLine.Offset, beepLine.Length); + Assert.StartsWith(" ", afterText); // indentation preserved + } + [Fact] public void UpdateSealedXml_DeadAnchor_ReturnsFalse() {