WinNotch

Documentation

Open repository

Using and building with WinNotch

WinNotch is a Windows overlay app built with WPF and .NET 8 that adds a dynamic-notch style surface to the edge of the screen. It combines built-in tools like music controls, file staging, battery, calendar, brightness and volume HUDs, then extends that surface with installable plugins.

This page is the technical walkthrough for both kinds of users: people who want to install and use the app well, and developers who want to build plugins that fit naturally into the notch. Everything here is based on the current WinNotch repository structure, the live plugin interfaces in src/WinNotch/Plugins, and the example plugins shipped in Examples/Plugins.

What WinNotch gives you

  • A compact notch that reacts to hover, click, scroll, and drag.
  • Built-in modules for media, volume, brightness, battery, calendar, webcam, and a file shelf.
  • A plugin ecosystem with UI, service, animation, and configurable plugins.

What this documentation covers

  • Installing WinNotch from releases or source.
  • Daily use, settings, plugin management, and updates.
  • Creating, packaging, configuring, and publishing plugins.

Setup overview

There are two common ways to get WinNotch running. The fastest route is the release installer from GitHub Releases. The more flexible route is cloning the repository and running the app directly from source. Both paths end up using the same settings folder in %AppData%\WinNotch, and both load plugins from %AppData%\WinNotch\Plugins.

Requirements

WinNotch targets Windows 10 and Windows 11 and is built on .NET 8 with WPF. For local development, install the .NET 8 SDK.

Install from a release

If you just want to use the app, start with the latest release. WinNotch publishes both a direct executable and an installer package.

  1. Open the latest release on GitHub.
  2. Download WinNotch-Setup.exe if you want the easiest install path.
  3. Run the installer, then launch WinNotch from Windows or from the installed executable.

Once launched, WinNotch places itself at the edge of the screen and keeps running from the tray. The app stores its persistent settings in the current user profile, not in the install folder, so upgrading to newer versions does not reset your layout or plugin data.

Recommended

Use the installer build unless you have a specific reason to carry the portable executable yourself. It gives a cleaner update path and is the expected experience for most users.

Build from source

Building from source is the best route if you want to modify the app, test unreleased changes, or author plugins directly inside the repository.

git clone https://github.com/N3uralCreativity/WinNotch.git
cd WinNotch
dotnet run --project src/WinNotch/WinNotch.csproj

To produce a publish folder instead of running the app in place:

dotnet publish src/WinNotch/WinNotch.csproj -c Release -r win-x64 --self-contained -p:PublishSingleFile=true -o publish

The repository already contains working example plugins, so cloning the repo also gives you a live plugin playground under Examples/Plugins.

First launch

On first launch, WinNotch creates its application data folder and defaults to the built-in behavior: hover to peek, click to open, scroll on the compact notch to control volume, and keep the rest of the UI out of the way until needed.

  • Hover gives a quick preview or starts a longer open interaction depending on your hover mode.
  • Click expands the notch into the full open state.
  • Ctrl+Alt+N toggles the notch globally.
  • Tray icon keeps the app accessible even when the main surface is hidden.
Settings file

The core application settings are stored in %AppData%\WinNotch\settings.json.

Update and uninstall

WinNotch includes an update check in the settings flow. When an update is available, the app downloads the installer and relaunches into the upgrade path. Plugin updates are handled separately from the Plugin Manager.

Updating the app

Use the built-in update action from settings or download a newer installer from Releases. The installer upgrade path preserves your settings and plugin folders.

Uninstalling the app

Removing WinNotch uninstalls the application binaries. During interactive uninstall, you can also choose whether to remove your stored data, which includes settings, plugin folders, and logs under the WinNotch app-data directories.

Daily use

WinNotch works best when you treat it as a quick interaction layer instead of a full window. Hover and click for short sessions, then let it collapse back out of the way.

Peek and open

Hover reveals a light preview. Click expands into the open layout where widgets, media controls, calendar, shelf, and plugins can live side by side.

Scroll controls

Scrolling the compact notch can control volume, and WinNotch shows an inline HUD for volume and brightness changes.

Shelf behavior

Drag files onto the notch to stage them, then drag them back out or open them later from the shelf.

Positioning and layout

WinNotch is not locked to one placement. It supports the classic top layout as well as side-oriented and vertical placements. That matters for both users and plugin authors because the open layout can change shape depending on where the notch is docked.

  • Top layout is the classic compact notch at the top center.
  • Side or vertical layout gives WinNotch room to open sideways and host plugins that provide a vertical-specific surface.
  • Accessory plugins can sit inline beside built-in content in the expanded top-row layout.
For plugin authors

If your plugin needs a different shape in side mode, expose UIPluginLocation.VerticalOpenContent instead of forcing the standard open layout to do everything.

Settings and storage

WinNotch keeps user data inside the roaming profile so the app can update without losing personal setup. The most important locations are:

Purpose Path
Main app settings %AppData%\WinNotch\settings.json
Installed plugins %AppData%\WinNotch\Plugins\
Per-plugin settings/data %AppData%\WinNotch\Plugins\<plugin-id>\
Plugin diagnostic log %AppData%\WinNotch\plugin.log

The core AppSettings model includes startup behavior, hover mode and delays, theme, corner radius scaling, shadow visibility, media and visualizer toggles, HUD toggles, calendar visibility, battery visibility, webcam visibility, and related preferences.

Built-in features

Before you install any plugins, WinNotch already includes a useful set of built-in services. These are also exposed to plugins through the plugin context.

Media

Reads the current media session, shows playback information, and exposes transport controls.

Theme

Supports Dark, Light, and Auto themes and lets plugins react when the app theme changes.

System HUDs

Volume and brightness feedback stays compact and can be toggled from settings.

Calendar and battery

Brings calendar and battery information into the open notch so plugins can live next to system data instead of replacing it.

Browse and install plugins

The Plugin Manager and Plugin Browser are the primary install flow for most people. WinNotch downloads manifests from the online library, lists available plugins by metadata, and installs the selected DLL into your local plugin directory.

  1. Open WinNotch settings.
  2. Open Plugin Manager.
  3. Select Browse Plugins.
  4. Install the plugin you want.
  5. Restart when WinNotch asks, especially for first install or queued updates.

Library installs also write a local manifest.json next to the DLL. That matters because the app uses that manifest to show installed metadata and compare versions when updates become available.

What powers the browser

The browser reads plugin metadata from the WinNotch-Plugins repository's library.json. A plugin only appears in the browser after its manifest entry is added there.

Manual plugin install

Manual install is still simple if you already have a compiled plugin DLL. Copy it into a folder named after the plugin ID under the WinNotch plugins directory, then restart WinNotch.

%AppData%\WinNotch\Plugins\com.example.myplugin\MyPlugin.dll

If you are distributing a plugin yourself, include a matching manifest.json in that same folder when possible. WinNotch can load a plugin from the DLL alone, but the manifest improves installed-state detection, update checks, and plugin metadata display.

Plugin updates and configuration

Installed plugins can be toggled, updated, and configured from Plugin Manager. If a plugin implements the configuration interface, the manager shows Set Up or Configure and opens a dedicated panel with fields, help text, and an Apply Configuration action.

How plugin updating works

If the current DLL is locked because WinNotch already loaded it, the download is written as a .pending file and applied automatically on the next startup. That is why a restart is part of the normal update flow.

What “requires configuration” means

Some plugins can run immediately. Others need API tokens, URLs, list names, timing values, or prompt presets before they make sense. Those plugins can mark themselves as requiring configuration and provide a schema for the built-in configuration UI.

Examples in this repository

Todo Peek, ChatGPT Add-on, and Focus Timer all use the configurable-plugin flow and store their values inside their own plugin folders under %AppData%\WinNotch\Plugins.

Plugin architecture

Every WinNotch plugin implements IPlugin directly or, more commonly, inherits from PluginBase. From there, a plugin can add one or more specialized interfaces depending on its role.

Interface Purpose
IPlugin Base lifecycle contract for every plugin.
IUIPlugin Injects WPF UI into closed, open, accessory, vertical, settings, or overlay locations.
IServicePlugin Runs background services without requiring visible UI.
IAnimationPlugin Customizes or replaces notch animations.
IConfigurablePlugin Exposes configuration fields to the Plugin Manager UI.
public interface IPlugin : IDisposable
{
    string Id { get; }
    string Name { get; }
    string Version { get; }
    string Author { get; }
    string Description { get; }
    string MinimumWinNotchVersion { get; }

    Task InitializeAsync(IPluginContext context);
    Task OnEnableAsync();
    Task OnDisableAsync();
    Task ShutdownAsync();
}

PluginBase provides default implementations and a Context property. In most cases, inheriting from it is the cleanest way to start.

Plugin attributes

WinNotch also provides attributes for plugin metadata, dependency declaration, and permission declaration. These are especially useful when you want the code to self-describe its intent alongside the runtime properties you expose.

[WinNotchPlugin("com.example.myplugin", "My Plugin", "1.0.0", "Your Name")]
[PluginDependency("com.example.sharedservice", "1.0.0")]
[PluginPermission("network", "Accesses an external weather API")]
public sealed class MyPlugin : PluginBase
{
}

Create your first plugin

The fastest way to start is to copy the structure used by the example plugins in this repository. They target the current Windows desktop target framework, enable WPF, and reference the WinNotch app project directly.

Project file

<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>

Minimal plugin class

using System.Windows;
using System.Windows.Controls;
using WinNotch.Models;
using WinNotch.Plugins;

namespace HelloWorldPlugin;

[WinNotchPlugin("com.winnotch.helloworld", "Hello World", "1.0.0", "WinNotch Team")]
public class HelloWorldPlugin : PluginBase, IUIPlugin
{
    public override string Id => "com.winnotch.helloworld";
    public override string Name => "Hello World";
    public override string Version => "1.0.0";
    public override string Author => "WinNotch Team";
    public override string Description => "A simple test plugin that displays system info in the notch.";
    public override string MinimumWinNotchVersion => "0.3.0";

    public UIElement? GetUIElement(UIPluginLocation location)
    {
        return location switch
        {
            UIPluginLocation.ClosedContent => new TextBlock { Text = "👋" },
            UIPluginLocation.OpenContent => new TextBlock { Text = "Hello from WinNotch" },
            _ => null
        };
    }

    public void OnNotchStateChanged(NotchState newState)
    {
    }
}

Build the plugin, copy the DLL into %AppData%\WinNotch\Plugins\com.winnotch.helloworld\, then restart WinNotch.

Alternative: consume the Plugin SDK package

If you do not want to clone the full WinNotch repository just to compile a plugin, use the GitHub Packages SDK package instead. It exposes the official WinNotch assembly as a compile-time package for plugin authors.

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-text

dotnet add package WinNotch.PluginSdk --version 0.6.2 --source github-winnotch

Use a personal access token with read:packages, and remember that this package is for developers only. End users should still install the WinNotch app from Releases.

UI plugins

UI plugins are the most common WinNotch extension type. They return WPF elements for specific locations and react when the notch state changes.

public interface IUIPlugin : IPlugin
{
    UIElement? GetUIElement(UIPluginLocation location);
    void OnNotchStateChanged(NotchState newState);
}

The current UI locations are:

  • ClosedContent for compact-notch UI.
  • OpenContent for the main expanded layout.
  • OpenAccessory for inline accessory content in the top-row expanded layout.
  • VerticalOpenContent for side or vertical expanded layouts.
  • CustomTab, Settings, and Overlay for more specialized surfaces.
Practical layout rule

If your plugin adds visible content in the expanded notch, always think about both top and vertical placements. The built-in examples like Weather, Todo Peek, Clipboard Stack, and Focus Timer all adapt their layouts rather than assuming a single horizontal card.

Theme-aware UI

Use Context.ThemeService from IPluginContext to inspect current theme state and react to changes. That lets your plugin stay readable across dark and light app themes without depending on hard-coded colors only.

Service and animation plugins

Not every plugin needs a visible panel. Service plugins are useful when you want background polling, integrations, or shared services that other plugins can consume. Animation plugins are for motion systems and custom expand, collapse, or peek behavior.

Service plugin shape

public interface IServicePlugin : IPlugin
{
    Task StartServiceAsync();
    Task StopServiceAsync();
}

Animation plugin shape

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; }
}

Use service plugins when the primary job is logic, polling, or integration work. Use animation plugins when the primary job is motion or transition style. A plugin can also combine roles if that makes sense, as long as the responsibilities still stay clear.

Configurable plugins

If your plugin needs user-provided values, implement IConfigurablePlugin. WinNotch will render a form in Plugin Manager using the definition returned by your plugin.

public interface IConfigurablePlugin : IPlugin
{
    PluginConfigurationDefinition GetConfigurationDefinition();
    Task<PluginConfigurationResult> ApplyConfigurationAsync(IReadOnlyDictionary<string, string?> values);
}

Each configuration definition is broken into sections and fields. Fields can be text, multiline text, numbers, or choices, and each field can include help text that explains what the user should enter and where to find it.

return new PluginConfigurationDefinition
{
    Title = "Todo Peek",
    Description = "Connect Todoist or Microsoft To Do.",
    StatusText = "Configuration required before tasks can load.",
    RequiresConfiguration = true,
    IsConfigured = false,
    Sections =
    [
        new PluginConfigurationSection
        {
            Title = "Provider",
            Fields =
            [
                new PluginConfigurationField
                {
                    Key = "provider",
                    Label = "Task provider",
                    FieldType = PluginConfigurationFieldType.Choice,
                    Options =
                    [
                        new PluginConfigurationOption { Value = "Todoist", Label = "Todoist" },
                        new PluginConfigurationOption { Value = "MicrosoftTodo", Label = "Microsoft To Do" }
                    ]
                }
            ]
        }
    ]
};

In your ApplyConfigurationAsync method, validate the submitted values, save them to the plugin data folder, and return a success or failure message. The manager displays that result directly to the user.

Plugin data, services, and publishing

WinNotch exposes a plugin context so your plugin can reach app services, create its own data folder, register shared services, and write diagnostics.

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);
}

Recommended data pattern

Store plugin-specific settings in the folder returned by GetPluginDataPath(Id). In practice that resolves to:

%AppData%\WinNotch\Plugins\<plugin-id>\

This is exactly how the example configurable plugins store their settings.json files today.

Publishing to the plugin library

To publish a plugin cleanly, host the DLL on a public release URL, compute its SHA-256 hash, then add a manifest entry to the WinNotch plugin library repository. The manifest shape matches the app’s PluginManifest model.

{
  "id": "com.example.myplugin",
  "name": "My Awesome Plugin",
  "version": "1.0.0",
  "author": "Your Name",
  "description": "Does amazing things",
  "minimumWinNotchVersion": "0.6.2",
  "downloadUrl": "https://github.com/you/myplugin/releases/download/v1.0.0/MyPlugin.dll",
  "homepage": "https://github.com/you/myplugin",
  "category": "Integration",
  "permissions": ["network"],
  "dependencies": [],
  "sha256": "abc123...",
  "releaseDate": "2026-04-23T00:00:00Z",
  "isVerified": false
}

Supported categories include Animation, Integration, Productivity, Media, SystemUtility, Theme, Widget, Fun, and Other.

The browser source of truth lives in WinNotch-Plugins/library.json. That repository also includes a contribution guide, a JSON schema, and a validation workflow so new entries can be checked before they ship.

git clone https://github.com/N3uralCreativity/WinNotch-Plugins.git
cd WinNotch-Plugins
powershell -ExecutionPolicy Bypass -File scripts\validate-library.ps1
Compatibility matters

If you raise MinimumWinNotchVersion, older app builds will refuse to load the plugin. That is correct behavior, but document the requirement clearly so users understand why a newer plugin may appear installed yet not load on an older app.

Important paths

When you are debugging, packaging, or helping someone install a plugin manually, these are the paths you will reach for most often.

%AppData%\WinNotch\settings.json
%AppData%\WinNotch\plugin.log
%AppData%\WinNotch\Plugins\
%AppData%\WinNotch\Plugins\plugin-states.json
%AppData%\WinNotch\Plugins\<plugin-id>\settings.json
%AppData%\WinNotch\Plugins\<plugin-id>\manifest.json

Inside the repository, these source locations matter most for plugin developers:

  • src/WinNotch/Plugins/ for the plugin contracts and lifecycle.
  • Examples/Plugins/ for working real examples.
  • PLUGIN_DEVELOPMENT.md for the repository’s plugin development guide.

Troubleshooting

Most WinNotch setup issues come down to version compatibility, missing files, or plugin data being placed in the wrong folder. The checklist below covers the common failure modes.

A plugin does not appear after restart

  • Confirm the DLL is inside %AppData%\WinNotch\Plugins\<plugin-id>\.
  • Check whether the plugin’s MinimumWinNotchVersion is higher than the WinNotch build you are running.
  • Open %AppData%\WinNotch\plugin.log and look for load failures or loader exceptions.

A plugin looks installed but the manager says zero plugins

This usually points to a compatibility mismatch. The plugin folder may still exist on disk, but the app will not load the plugin if its minimum supported version is newer than the current WinNotch build.

A plugin update does not apply immediately

That is expected when the current DLL is still in use. WinNotch queues the update as a .pending file and applies it on the next launch.

The plugin browser does not show a brand-new release yet

Refresh the library and give the raw GitHub library feed a moment to update. Plugin metadata comes from the online library.json, so a short propagation delay can happen after a release.

A configurable plugin says it is not set up

Open Plugin Manager, select Set Up or Configure, fill every required field, and apply the configuration. If the plugin also stores a settings.json file, make sure it contains valid JSON.