Thank you for your interest in contributing! ??
This guide will help you get started with the Universal Device Toolkit plugin ecosystem.
- Ways to Contribute
- Development Setup
- Plugin Development Guide
- Coding Standards
- UI Guidelines
- Pull Request Process
- Community
There are many ways to contribute ��no contribution is too small!
- Use the GitHub Issues page
- Check if the bug has already been reported
- Include: OS version, .NET version, steps to reproduce, expected vs actual behavior
- Open a Feature Request
- Describe the use case and why it would be valuable
- Fix bugs, implement features, improve documentation
- Follow the coding standards below
- Test your changes before submitting
- Fix typos, clarify wording, add examples
- Translate to other languages (we currently support English + Chinese)
- Build a new plugin using our SDK
- Two paths: Contributor Path (community plugins) or Official Store Path (official plugins)
- Windows 10/11 (x64)
- .NET 10 SDK or later
- Visual Studio 2022 (17.8+) or Visual Studio Code
- Git
# Clone the repository
git clone https://github.com/SSC-STUDIO/UniversalDeviceToolkit-Plugins.git
cd UniversalDeviceToolkit-Plugins
# Restore NuGet packages
dotnet restore
# Build the solution
dotnet build
# Or use the provided build script
.\Make.bat# Launch the standalone Plugin Workbench
cd Tools/PluginWorkbench
dotnet run
# In the Workbench:
# 1. Click "Load Plugin"
# 2. Select the plugin's build output ZIP or DLL
# 3. Switch between Light/Dark themes to verify UI
# 4. Test feature and settings pages# Use the plugin tooling CLI to scaffold a new plugin
cd Tools/PluginTooling.Cli
dotnet run -- init MyPluginThis creates a new plugin at Plugins/MyPlugin/ with the standard structure.
This repository supports two paths:
Use this path for community-contributed plugins.
- Run
doctorto check environment - Scaffold with
new - Build with
build - Preview with
preview - Validate with
validate --profile contributor - Pack with
pack
You do not need store-entry.json for this path.
Only use this path when the plugin is meant to ship from the official repository.
Additional requirements:
store-entry.json- Plugin
CHANGELOG.md - Root
CHANGELOG.mdentry - Successful
official-candidatevalidation
Plugins/MyPlugin/
������� MyPlugin.cs # Plugin entry point (implements IPlugin)
������� MyPluginPlugin.cs # Plugin attribute and metadata
������� MyPluginPage.xaml # Feature page UI (optional)
������� MyPluginPage.xaml.cs # Feature page code-behind
������� MyPluginSettingsControl.xaml # Settings page UI
������� MyPluginSettingsControl.xaml.cs # Settings page code-behind
������� Resources/
�? ������� Resource.resx # Localized strings
������� plugin.manifest.json # Plugin manifest (authoring)
������� store-entry.json # Store metadata (official only)
������� UniversalDeviceToolkit.Plugins.MyPlugin.csproj
using UniversalDeviceToolkit.Plugins.SDK;
[Plugin(
Id = "my-plugin",
Name = "My Plugin",
Description = "Does awesome things",
Author = "Your Name",
Version = "1.0.0"
)]
public class MyPlugin : IPlugin
{
public string Id => "my-plugin";
public string Name => "My Plugin";
public string Description => "Does awesome things";
public string Author => "Your Name";
public string Version => "1.0.0";
public void Initialize(IPluginHostContext context)
{
// Initialize your plugin
}
public void Shutdown()
{
// Clean up resources
}
}-
Use DynamicResource for all colors ��zero hardcoded brushes:
<Border Background="{DynamicResource ControlFillColorDefaultBrush}" BorderBrush="{DynamicResource ControlStrokeColorDefaultBrush}">
-
Use x:Static for all text ��zero hardcoded strings:
<TextBlock Text="{x:Static resources:Resource.MyPlugin_Title}" />
-
Provide fallback UI ��implement
BuildFallbackUi():public MyPluginPage() { WpfFallbackHelper.TryInitializeComponent(this, BuildFallbackUi); } private void BuildFallbackUi() { // Construct UI programmatically with same structure as XAML }
-
Add AutomationIds for testing:
AutomationProperties.SetAutomationId(myButton, "MyPluginActionButton");
-
Use Grid star-sizing for responsive layouts
- Don't hardcode colors (e.g.,
Foreground="White"��use DynamicResource instead) - Don't hardcode strings in XAML or code-behind
- Don't skip fallback UI ��always implement
BuildFallbackUi() - Don't forget to test in both Light and Dark themes
- Follow Microsoft's C# Coding Conventions
- Use 4 spaces for indentation (no tabs)
- Use PascalCase for public members, camelCase for private fields
- Use meaningful names ��avoid abbreviations
- Add XML documentation for public APIs
Follow the Conventional Commits specification:
<type>(<scope>): <subject>
Examples:
feat(network-acceleration): add peak traffic card to dashboard
fix(vive-tool): resolve feature flag status parsing for Windows 11 24H2
docs(readme): update installation instructions
chore(build): bump .NET SDK to 10.0.100
- Code builds without errors (
dotnet build) - All tests pass (
dotnet test) - Plugin Workbench verified (Light + Dark themes)
-
CHANGELOG.mdupdated (under[Unreleased]) -
plugin.manifest.jsonupdated (if plugin metadata changed) - Commit messages follow conventional commits
- PR description explains what and why
PRs should include:
- Plugin source code
- Test project (for official plugins)
plugin.manifest.json- Plugin
CHANGELOG.md(for official candidates) store-entry.json(only for official store path)
- Do not start new authoring by editing root
store.jsondirectly - Do not add source references back to the sibling main repo
- Do not bypass
PluginWorkbenchwhen checking host-facing UI
- GitHub Issues: Bug reports & feature requests
- GitHub Discussions: Q&A and community chat
- Releases: GitHub Releases page
Contributors are recognized in:
- Release notes (for significant contributions)
- The project README (for plugin authors)
- The CHANGELOG.md
Thank you for contributing to UniversalDeviceToolkit-Plugins! ??