This guide covers the current WinNotch plugin workflow: how to reference the plugin API, build a plugin, test it locally, and get it listed in the in-app browser.
If you want the broader user and developer walkthrough, see the live documentation site:
https://n3uralcreativity.github.io/WinNotch/documentation/
WinNotch plugins can extend the app in several ways:
- add visible UI to the closed or expanded notch
- add accessory content beside built-in expanded content
- provide a different vertical layout when the notch is docked on the side
- run background services or integrations
- replace or extend notch animations
- expose a configuration form inside Plugin Manager
Most plugins inherit from PluginBase and then implement one or more specialized interfaces.
These are the main extension points in the current app:
IPluginIUIPluginIServicePluginIAnimationPluginIConfigurablePlugin
UI plugins can return elements for these locations:
ClosedContentOpenContentOpenAccessoryVerticalOpenContentCustomTabSettingsOverlay
The live contracts are in src/WinNotch/Plugins/.
There are two good ways to reference WinNotch when building plugins.
This is the easiest setup if you are building a plugin outside this repository.
Add the GitHub Packages source:
dotnet nuget add source "https://nuget.pkg.github.com/N3uralCreativity/index.json" \
--name github-winnotch \
--username YOUR_GITHUB_USERNAME \
--password YOUR_GITHUB_PAT \
--store-password-in-clear-textThen install the SDK package:
dotnet add package WinNotch.PluginSdk --version 0.6.2 --source github-winnotchYour personal access token needs read:packages.
This is the best setup when you are already working inside the WinNotch repository and want easy access to the example plugins.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\WinNotch\WinNotch.csproj">
<Private>false</Private>
<ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference>
</ItemGroup>
</Project>Whether you use the package or a direct project reference, your plugin project should look roughly like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="WinNotch.PluginSdk" Version="0.6.2" />
</ItemGroup>
</Project>using System.Windows;
using System.Windows.Controls;
using WinNotch.Models;
using WinNotch.Plugins;
namespace HelloWorldPlugin;
[WinNotchPlugin("com.example.helloworld", "Hello World", "1.0.0", "Your Name")]
public sealed class HelloWorldPlugin : PluginBase, IUIPlugin
{
public override string Id => "com.example.helloworld";
public override string Name => "Hello World";
public override string Version => "1.0.0";
public override string Author => "Your Name";
public override string Description => "A simple test plugin.";
public override string MinimumWinNotchVersion => "0.6.2";
public UIElement? GetUIElement(UIPluginLocation location)
{
return location switch
{
UIPluginLocation.ClosedContent => new TextBlock { Text = "Hi" },
UIPluginLocation.OpenContent => new TextBlock { Text = "Hello from WinNotch" },
_ => null
};
}
public void OnNotchStateChanged(NotchState newState)
{
}
}Build it with:
dotnet build -c ReleaseFor a manual local install, place the DLL under:
%AppData%\WinNotch\Plugins\com.example.helloworld\HelloWorldPlugin.dll
Restart WinNotch after copying the file.
If you also include a manifest.json beside the DLL, WinNotch can show better installed metadata and compare versions for updates.
Every plugin receives an IPluginContext during initialization. It gives access to WinNotch services and plugin storage.
public interface IPluginContext
{
Window MainWindow { get; }
NotchViewModel NotchViewModel { get; }
MediaService MediaService { get; }
ThemeService ThemeService { get; }
VolumeService VolumeService { get; }
BrightnessService BrightnessService { get; }
BatteryService BatteryService { get; }
CalendarService CalendarService { get; }
AudioCaptureService AudioCaptureService { get; }
ShelfService ShelfService { get; }
WebcamService WebcamService { get; }
FullscreenService FullscreenService { get; }
AppSettings Settings { get; }
string GetPluginDataPath(string pluginId);
void RegisterService<T>(T service) where T : class;
T? GetService<T>() where T : class;
void Log(string message, PluginLogLevel level = PluginLogLevel.Info);
}Use GetPluginDataPath(Id) for plugin-owned files such as settings.json, caches, or tokens.
IUIPlugin is the most common interface:
public interface IUIPlugin : IPlugin
{
UIElement? GetUIElement(UIPluginLocation location);
void OnNotchStateChanged(NotchState newState);
}Layout advice:
- use
OpenAccessoryfor small inline content beside built-in expanded content - use
VerticalOpenContentwhen a side-docked notch needs a different composition - avoid assuming a fixed horizontal canvas unless your plugin is explicitly top-only
- react to
Context.ThemeService.ThemeChangedso your UI stays readable in both themes
Use IServicePlugin for background work:
public interface IServicePlugin : IPlugin
{
Task StartServiceAsync();
Task StopServiceAsync();
}Use IAnimationPlugin when your plugin primarily controls notch motion:
public interface IAnimationPlugin : IPlugin
{
Storyboard? CreateExpandAnimation(FrameworkElement target, double fromWidth, double toWidth, double fromHeight, double toHeight);
Storyboard? CreateCollapseAnimation(FrameworkElement target, double fromWidth, double toWidth, double fromHeight, double toHeight);
Storyboard? CreatePeekAnimation(FrameworkElement target);
IEasingFunction? GetCustomEasingFunction();
double AnimationDurationMs { get; }
bool ReplaceDefaultAnimations { get; }
}If your plugin needs user input, implement IConfigurablePlugin.
public interface IConfigurablePlugin : IPlugin
{
PluginConfigurationDefinition GetConfigurationDefinition();
Task<PluginConfigurationResult> ApplyConfigurationAsync(IReadOnlyDictionary<string, string?> values);
}The Plugin Manager will render a built-in configuration panel using the fields you describe. Each field can include:
- label
- placeholder
- help text
- required state
- choice options
Typical use cases:
- API keys
- account or workspace identifiers
- provider selection
- timer values
- prompt templates
The normal plugin release flow is:
- Build your plugin DLL in
Release. - Upload that DLL to a public release URL, usually a GitHub Release.
- Compute its SHA-256 hash.
- Create or update a plugin manifest entry.
- Add that entry to
WinNotch-Plugins/library.jsonif you want it to appear in the in-app browser.
Example manifest:
{
"id": "com.example.myplugin",
"name": "My Plugin",
"version": "1.0.0",
"author": "Your Name",
"description": "Does something useful in the notch.",
"minimumWinNotchVersion": "0.6.2",
"downloadUrl": "https://github.com/you/myplugin/releases/download/v1.0.0/MyPlugin.dll",
"homepage": "https://github.com/you/myplugin",
"iconUrl": "",
"category": "Productivity",
"permissions": ["network"],
"dependencies": [],
"sha256": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF",
"releaseDate": "2026-04-23T00:00:00Z",
"isVerified": false
}The WinNotch app does not search GitHub releases directly. The in-app browser reads plugin metadata from the WinNotch-Plugins repository's library.json.
That means a plugin appears in the browser only when:
- the DLL is publicly downloadable
- the manifest entry is present in
WinNotch-Plugins/library.json - the entry passes the library validation checks in that repository
The plugin library repo now includes:
library.schema.jsonCONTRIBUTING.md- a validation script at
scripts/validate-library.ps1 - a GitHub Actions workflow that validates
library.jsonon push and pull request
Before opening a PR to WinNotch-Plugins, confirm:
- your plugin ID is unique
- your version is updated
minimumWinNotchVersionmatches the APIs you actually usedownloadUrlpoints directly to the DLLhomepagepoints to the repo or project page- the SHA-256 hash matches the released DLL
- your category is one of the supported values
- the plugin has been tested in WinNotch
Supported categories:
AnimationIntegrationProductivityMediaSystemUtilityThemeWidgetFunOther
Working examples live under Examples/Plugins/, including:
BetterAnimationPluginChatGPTAddonPluginPetWidgetPluginWeatherWidgetPluginTodoPeekPluginClipboardStackPluginFocusTimerPluginDownloadsWatcherPlugin
There is also a sample browser manifest file at:
Examples/Plugins/example-library.json
- use semantic versioning for your plugin versions
- keep plugin-specific files inside the folder returned by
GetPluginDataPath - avoid blocking the UI thread
- clean up timers, subscriptions, and unmanaged resources in
ShutdownAsyncandDispose - handle theme changes and layout changes explicitly
- document any required setup clearly if your plugin is configurable
- raise
MinimumWinNotchVersionwhen you depend on newer WinNotch APIs
- Main repo issues:
https://github.com/N3uralCreativity/WinNotch/issues - Plugin browser listings:
https://github.com/N3uralCreativity/WinNotch-Plugins