Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,14 @@ 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`.
5. Build the DLL and drop it in the `plugins/` directory.

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.
Expand Down
6 changes: 3 additions & 3 deletions docs/plugins/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="path/to/SharpFM.Plugin.csproj" />
Expand Down Expand Up @@ -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.
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/ClipContentChangedArgs.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/IClipTransformPlugin.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/IEventPlugin.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/IPanelPlugin.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/IPersistencePlugin.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/IPlugin.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/IPluginHost.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/PluginKeyBinding.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 0 additions & 5 deletions src/SharpFM.Plugin/PluginMenuAction.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/SharpFM/Editors/ScriptClipEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
20 changes: 20 additions & 0 deletions tests/SharpFM.Tests/Scripting/SealedStepPreservationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"<Step enable=\"True\" id=\"93\" name=\"Beep\"><SomeChild value=\"1\"/></Step>");
editor.UpdateSealedXml(anchor, replacement);

var afterText = editor.Document.GetText(beepLine.Offset, beepLine.Length);
Assert.StartsWith(" ", afterText); // indentation preserved
}

[Fact]
public void UpdateSealedXml_DeadAnchor_ReturnsFalse()
{
Expand Down
Loading