Welcome, developer!
This guide will walk you through creating your own plugins to extend the functionality of WinTool. The plugin system is designed to be simple yet powerful, allowing you to build rich system utilities using standard web technologies.
- 🛠️ Plugin CLI Tool
- 🚀 Getting Started
- 🏗️ Plugin Anatomy
- 🌉 The Plugin API Reference
- 🔒 Enhanced Security Model
- 🎨 UI Component Guide
- ✅ Development Best Practices
- 📦 Distributing Your Plugin
- 📦 Backend Dependency Management (npm)
Before you begin developing or testing plugins, it is highly recommended that you enable Developer Mode. This mode provides access to several tools that are invaluable for debugging and performance monitoring.
Developer tools include:
- In-App Log Viewer: A separate window that displays real-time
console.logmessages from both the main and renderer processes. - Performance Overlay: A small overlay in the bottom-right corner of the application that shows real-time FPS, CPU, and memory usage.
How to Enable:
- Open WinTool and click the Settings button in the sidebar.
- Navigate to the Application settings panel.
- Check the box for Enable Developer Tools.
- Save your settings. A restart may be required for all developer features to become active.
WinTool now includes a powerful command-line tool for plugin development that streamlines the entire development workflow from creation to distribution.
🔘 Important: For detailed information about how buttons work in plugins, ensure you use container-scoped element selection (see Troubleshooting section)
cd CLI
npm install -g .Or use the PowerShell installer:
.\CLI\install.ps1After installation, you can use the CLI from anywhere:
wintool-plugin-cli --help🔘 Critical: Buttons in WinTool plugins require specific patterns to work. Use container-scoped element selection instead of
document.getElementById()(see Troubleshooting section for details).
The CLI supports three plugin types:
wintool-plugin-cli create my-plugin --type=basicCreates a frontend-only plugin with HTML, CSS, and JavaScript.
wintool-plugin-cli create my-plugin --type=advanced --author="Your Name"Creates a plugin with backend support and Node.js integration.
wintool-plugin-cli create my-plugin --type=minimalCreates a bare minimum plugin structure.
--author=<name>: Set plugin author--description=<desc>: Set plugin description--version=<version>: Set plugin version--icon=<icon>: Set Font Awesome icon class--dev: Create in development directory
wintool-plugin-cli validate ./my-pluginChecks file structure, manifest format, and code quality.
wintool-plugin-cli security ./my-pluginPerforms comprehensive security analysis including:
- Dangerous code pattern detection
- External resource scanning
- Dependency analysis
- File permission checks
wintool-plugin-cli test ./my-pluginExecutes automated tests for validation, security, and functionality.
wintool-plugin-cli build ./my-pluginCreates a ZIP package ready for distribution with integrity hash generation.
wintool-plugin-cli listShows all installed plugins with metadata and validation status.
⚡ Important: The CLI now generates plugins with working buttons out of the box! All generated code follows the correct patterns for WinTool's tab system.
The fastest way to start developing is with the CLI tool:
# Create a new plugin
wintool-plugin-cli create my-awesome-plugin --type=advanced --author="Your Name"
# Navigate to plugin directory
cd my-awesome-plugin
# Validate your plugin
wintool-plugin-cli validate
# Test your plugin
wintool-plugin-cli test
# Build for distribution
wintool-plugin-cli buildThe easiest way to start is by using the CLI tool to generate a template.
- Generate Template: Use the CLI to create a new plugin with a template structure:
wintool-plugin-cli create my-cool-plugin --type=basic
- Navigate to Plugin: The plugin will be created in
%LOCALAPPDATA%\MTechWare\WinTool\Plugins\my-cool-plugin\ - Customize the Manifest: Open
plugin.jsoninside your new plugin folder and edit thename,description,author, andiconfields. - Start Coding: Open
index.html,script.js, andstyles.cssto begin building your plugin. The generated template provides a solid foundation and working examples. - Run WinTool: The application will automatically detect and load your new plugin.
If you prefer to start from scratch:
- Create a Folder: Create a new folder for your plugin inside
%LOCALAPPDATA%\MTechWare\WinTool\Plugins\. - Create Core Files: Inside the new folder, create
plugin.json,index.html,script.js, andstyles.css. - Populate Files: Add the basic content to each file.
- Run WinTool: The application will load your plugin.
🔧 Key Insight: WinTool plugins work like tabs, not standalone web pages. They must use container-scoped element selection and follow specific initialization patterns.
WinTool organizes all its data in a dedicated directory structure:
%LOCALAPPDATA%\MTechWare\WinTool\ # Main application directory
├── WinTool.exe # Application executable
├── config.json # Application settings and preferences
└── Plugins\ # User-installed plugins directory
├── my-cool-plugin\ # Individual plugin directory
│ ├── plugin.json # Plugin manifest
│ ├── index.html # Plugin UI
│ ├── script.js # Plugin frontend logic
│ ├── styles.css # Plugin styles
│ ├── backend.js # Plugin backend logic (optional)
│ ├── package.json # Backend dependencies (optional)
│ └── node_modules\ # Plugin-specific dependencies
└── another-plugin\ # Another plugin directory
└── ...
Every plugin is a directory inside %LOCALAPPDATA%\MTechWare\WinTool\Plugins\ and must contain these core files:
%LOCALAPPDATA%\MTechWare\WinTool\Plugins\
└── my-cool-plugin/
├── plugin.json // (Required) Manifest file with metadata.
├── index.html // (Required) The UI of the plugin.
├── script.js // (Required) The logic for the UI.
├── styles.css // (Optional) The styling for the UI.
├── backend.js // (Optional) The Node.js backend logic.
└── package.json // (Optional) For backend dependencies.
This file tells WinTool how to load your plugin.
{
"name": "My Cool Plugin",
"icon": "fas fa-star",
"backend": "backend.js",
"description": "A brief description of what this plugin does.",
"version": "1.0.0",
"author": "Your Name"
}Manifest Fields:
name(string, required): The human-readable name of your plugin, which will appear in the sidebar.icon(string, required): The Font Awesome icon class (e.g.,"fas fa-cogs").backend(string, optional): The path to the plugin's backend script. If provided, WinTool will load this script in the main process.description(string, optional): A brief summary of your plugin's functionality.version(string, optional): The version of your plugin (recommended: semantic versioning).author(string, optional): Your name or username.permissions(array, optional): List of permissions required by your plugin (see Security Model).dependencies(object, optional): External dependencies required by your plugin.
🔘 Button Implementation: Before diving into the API, ensure your buttons work correctly. Use container-scoped element selection instead of
document.getElementById()for proper tab functionality.
WinTool provides a rich, secure API to plugins. The API is split into two parts: a Frontend API available in your script.js, and a Backend API available in your backend.js.
This object is exposed globally in your plugin's renderer process (script.js). It is the primary way for your UI to interact with the WinTool application and its secure main process. All methods return a Promise.
The most important frontend function. It calls a function (handler) that you have registered in your plugin's backend.js.
pluginId(string): Your plugin's folder name.handlerName(string): The name of the handler to call....args: Any arguments to pass to the backend handler.- Returns: A
Promisethat resolves with the value returned by the backend handler.
// In script.js
const result = await window.wintoolAPI.invoke('my-plugin', 'my-handler', 'arg1', 2);
console.log(result); // Logs the value returned from the backend.Fetches a specific category of system information.
type(string): The type of information to get (e.g.,'cpu','mem','osInfo').- Returns: A
Promisethat resolves with the requested system information object.
const cpuInfo = await window.wintoolAPI.getSystemInfo('cpu');
console.log(`CPU Brand: ${cpuInfo.brand}`);Securely executes a PowerShell script that is bundled with your plugin.
pluginId(string): Your plugin's folder name.scriptPath(string): The relative path to the script inside your plugin's folder.- Returns: A
Promisethat resolves with the script's standard output.
const output = await window.wintoolAPI.runPluginScript('my-plugin', 'scripts/my-script.ps1');
console.log(output);Displays a native-style notification.
options(object):title(string): The notification title.body(string): The main content of the notification.type(string, optional):'info','success','warning', or'error'. Defaults to'info'.
window.wintoolAPI.showNotification({
title: 'Success!',
body: 'The operation completed successfully.',
type: 'success'
});Retrieves a value from your plugin's persistent, namespaced storage.
pluginId(string): Your plugin's folder name.key(string): The key of the data to retrieve.- Returns: A
Promiseresolving to the stored value, ornullif not found.
const mySetting = await window.wintoolAPI.storage.get('my-plugin', 'user-preference');Saves a value to your plugin's persistent, namespaced storage.
pluginId(string): Your plugin's folder name.key(string): The key to save the data under.value(any): The value to store.- Returns: A
Promisethat resolves when the operation is complete.
await window.wintoolAPI.storage.set('my-plugin', 'user-preference', { theme: 'dark' });Shows a native file open dialog.
options(object): ElectronshowOpenDialogoptions.- Returns: A
Promiseresolving to{ canceled, file }.fileis{ path, content }.
const result = await window.wintoolAPI.dialog.showOpenDialog({ title: 'Open My Data File' });
if (!result.canceled) {
console.log(result.file.content);
}Shows a native file save dialog.
options(object): ElectronshowSaveDialogoptions.content(string): The string content to write to the file if saved.- Returns: A
Promiseresolving to{ canceled, path }.
const result = await window.wintoolAPI.dialog.showSaveDialog({ title: 'Save Report' }, 'My report data');
if (!result.canceled) {
console.log(`File saved to ${result.path}`);
}Lets you listen to events happening in the application, such as tab switching.
eventName(string): The name of the event (e.g.,'tab-switched').callback(function): The function to execute when the event occurs.
function handleTabSwitch(event) {
console.log(`Switched to tab: ${event.detail.newTabId}`);
}
// Add listener
window.wintoolAPI.tabs.on('tab-switched', handleTabSwitch);
// Remove listener
window.wintoolAPI.tabs.off('tab-switched', handleTabSwitch);If your plugin includes a backend.js file, it must export an initialize function. This function receives a secure api object with methods for interacting with the system and registering handlers.
// my-plugin/backend.js
module.exports = {
initialize: (api) => {
// Use the api object here
}
};Registers a function that can be called from the frontend using wintoolAPI.invoke().
name(string): The name of the handler.func(function): The function to execute. Can beasync.
// In backend.js
api.registerHandler('get-data', async (someId) => {
// do something with someId
return { message: 'Data retrieved!' };
});Loads a dependency from your plugin's local node_modules directory. See the "Backend Dependency Management" section for more details.
moduleName(string): The name of the npm package to load.- Returns: The loaded module.
// In backend.js
const _ = api.require('lodash');
const sorted = _.sortBy([3, 1, 2]); // Returns [1, 2, 3]A pre-configured instance of the axios library for making HTTP requests from your backend.
// In backend.js
api.registerHandler('fetch-user', async () => {
const response = await api.axios.get('https://randomuser.me/api/');
return response.data.results[0];
});Gets an instance of the electron-store object, allowing you to access the same persistent storage as the frontend API.
- Returns: A
Promisethat resolves with the store instance.
// In backend.js
api.registerHandler('save-backend-setting', async (value) => {
const store = await api.getStore();
store.set('my-plugin_internal-setting', value);
});The raw Electron dialog object. This allows your backend to show message boxes or other dialogs directly.
// In backend.js
api.registerHandler('show-error', async (message) => {
await api.dialog.showErrorBox('An Error Occurred', message);
});WinTool features a comprehensive multi-layered security system that protects users while enabling powerful plugin functionality.
Each plugin runs in an isolated sandbox with the following protections:
- Memory Usage: Configurable per plugin (default: 50MB, trusted: 100MB)
- Execution Time: Maximum runtime limits (default: 30s, trusted: 60s)
- API Call Limits: Rate limiting on API calls to prevent abuse
- Network Requests: Controlled and monitored HTTP requests
- Separate Context: Each plugin runs in its own isolated JavaScript context
- API Mediation: All system access goes through secure API wrappers
- Module Sandboxing: Backend modules are loaded in controlled environments
Plugins must declare required permissions in their manifest:
{
"name": "My Plugin",
"permissions": [
"storage.read",
"storage.write",
"notifications.show",
"network.request",
"fs.readUserFile"
]
}storage.read: Read from plugin storagestorage.write: Write to plugin storagenotifications.show: Display notificationsnetwork.request: Make HTTP requestsfs.readUserFile: Access user-selected filessystem.info: Access system information
Plugins are assigned security policies based on trust level:
- Limited memory (50MB) and execution time (30s)
- Basic permissions only
- No external network access
- Strict code analysis
- Higher resource limits (100MB, 60s)
- Extended permissions
- Allowed domains for network requests
- Relaxed restrictions for verified plugins
- Minimal resources (25MB, 15s)
- Read-only permissions
- No network access
- Enhanced monitoring
The security system enforces both per-plugin and global limits:
- Memory usage monitoring
- CPU time tracking
- Network request quotas
- File access logging
- Maximum active plugins (50)
- Total memory usage (500MB)
- Concurrent network requests (10)
All plugins undergo comprehensive security analysis:
- Dangerous pattern detection (eval, Function constructor)
- External resource scanning
- Inline event handler detection
- JavaScript protocol usage
- API usage pattern analysis
- Resource consumption tracking
- Suspicious activity detection
- Violation response handling
Plugins cannot access the file system directly. They must use the secure file access APIs that require user interaction, ensuring users maintain control over their data.
To ensure a consistent look and feel, plugins are encouraged to use the application's built-in UI components. These styles are available automatically to your plugin.
Use for any action a user can take.
- Primary Action:
.btn .btn-primary(e.g., "Run", "Save") - Secondary Action:
.btn .btn-secondary(e.g., "Cancel", "Export") - Success Action:
.btn .btn-success(e.g., "Add", "Start") - Destructive Action:
.btn .btn-danger(e.g., "Delete", "Stop")
Example:
<button class="btn btn-primary">
<i class="fas fa-play"></i> Run Script
</button>Cards are used to group related content into modular blocks.
Example:
<div class="plugin-card">
<div class="plugin-card-header">
<i class="fas fa-cogs"></i>
<h4>My Awesome Plugin</h4>
</div>
<p>This is a description of what my plugin does.</p>
<div class="plugin-card-footer">
<span>Version 1.0.0</span>
<span>by Developer</span>
</div>
</div>Use these classes to create styled inputs for collecting user data.
- Container:
.form-group - Text Input:
.form-input - Dropdown:
.settings-select - Checkbox:
.settings-checkbox
Example:
<div class="form-group">
<label for="my-input">My Setting</label>
<input type="text" id="my-input" class="form-input" placeholder="Enter a value...">
</div>Modals are used to display content or forms in a focused overlay. You will need to use JavaScript to toggle the display style between none and flex to show and hide them.
Example:
<div id="my-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>My Modal</h3>
<button class="modal-close" onclick="hideModal()">×</button>
</div>
<div class="modal-body">
<p>This is the content of my modal.</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" onclick="hideModal()">Close</button>
</div>
</div>
</div>The application uses custom-styled tables per tab (e.g., .services-table). It's recommended to define a simple table style in your plugin's own styles.css for consistency.
- Use the CLI Tool: Start with
wintool-plugin-cli createfor consistent project structure - Validate Early: Run
wintool-plugin-cli validatefrequently during development - Security First: Use
wintool-plugin-cli securityto catch security issues early - Test Thoroughly: Run
wintool-plugin-cli testbefore distribution
- Use CSS Variables: Use the application's CSS variables (e.g.,
var(--primary-color),var(--background-card)) for consistent theming - Handle Asynchronous Operations: All
wintoolAPIcalls are asynchronous. Useasync/awaitfor clean code - Signal Tab Readiness: Call
window.markTabAsReady(tabId)once your tab is loaded - Error Handling: Always wrap API calls in try-catch blocks
- Input Validation: Validate all user inputs before processing
- Declare Permissions: Only request permissions your plugin actually needs
- Avoid Dangerous Patterns: Never use
eval(),Function()constructor, orinnerHTMLwith user data - Sanitize Inputs: Always sanitize user inputs and external data
- Use Secure APIs: Use the provided secure APIs instead of direct system access
- Minimize Dependencies: Only include necessary dependencies to reduce attack surface
- Resource Management: Monitor memory usage and clean up resources
- Lazy Loading: Load resources only when needed
- Efficient DOM Updates: Minimize DOM manipulations and use document fragments
- Debounce Events: Debounce frequent events like input changes
- Cache Results: Cache expensive computations and API results
The CLI tool provides the best way to package and distribute plugins:
# Build your plugin
wintool-plugin-cli build ./my-plugin
# This creates:
# - A ZIP package ready for distribution
# - An integrity hash for verification
# - Validation reportTo manually share your plugin:
- Validate First: Ensure your plugin passes validation
- Create ZIP: Compress your plugin folder into a ZIP file
- Include Documentation: Add a README with installation instructions
- Share: Distribute the ZIP file to users
Users can install plugins in several ways:
- Open WinTool Settings
- Go to Plugins section
- Click "Install Plugin"
- Select the ZIP file
- Extract ZIP to
%LOCALAPPDATA%\MTechWare\WinTool\Plugins - Restart WinTool
- Plugin appears in sidebar
WinTool includes a verification system:
- Hash Verification: Plugins are verified against known good hashes
- Security Scanning: Automatic security analysis on installation
- Trust Levels: Plugins can be marked as trusted, default, or restricted
- Update Notifications: Users are notified of plugin updates
For plugins that require a backend.js, you can manage dependencies using npm. This is the recommended approach for any backend development, as it is more robust and scalable than manual library management.
-
Create
package.json: In the root of your plugin's folder, create a standardpackage.jsonfile. -
Add Dependencies: Add your required packages to the
dependenciessection.{ "name": "my-cool-plugin", "version": "1.0.0", "dependencies": { "lodash": "^4.17.21", "uuid": "^9.0.0" } } -
Automatic Installation: When WinTool starts, it will automatically detect the
package.jsonfile and runnpm installinside your plugin's directory if it finds that thenode_modulesfolder is missing.
To maintain security and prevent conflicts between plugins, you cannot use a global require() in your backend.js. Instead, the initialize function of your backend is passed a secure api object which contains a special require function.
api.require(moduleName): Loads a module from your plugin's localnode_modulesdirectory.
Here is how you would load the lodash and uuid packages defined in the package.json above.
// my-cool-plugin/backend.js
module.exports = {
initialize: (api) => {
// Load dependencies using the provided API
const _ = api.require('lodash');
const { v4: uuidv4 } = api.require('uuid');
// Register a handler that uses a dependency
api.registerHandler('get-unique-id', async () => {
return uuidv4();
});
// Register another handler using a different dependency
api.registerHandler('sort-array', async (data) => {
if (!Array.isArray(data)) {
throw new Error('Data must be an array.');
}
return _.sortBy(data);
});
}
};- Root cause: Using
document.getElementById()instead ofcontainer.querySelector() - Why it happens: WinTool plugins work like tabs and need container scoping
- Quick fix: Replace all
document.getElementById()withcontainer.querySelector() - Complete solution: Use container-scoped queries:
container.querySelector('#my-element')instead ofdocument.getElementById('my-element') - Generated code: New CLI plugins have working buttons by default
- Check that
plugin.jsonis valid JSON - Ensure all required files are present
- Verify plugin directory is in correct location
- Check console for error messages
- Review security scan results with
wintool-plugin-cli security - Remove dangerous code patterns (eval, Function constructor)
- Declare required permissions in manifest
- Use secure APIs instead of direct system access
- Monitor resource usage in developer tools
- Check for memory leaks in long-running plugins
- Optimize DOM operations and event handlers
- Use the CLI validation to identify performance issues
- Ensure all dependencies are installed
- Check that plugin passes validation
- Verify security scan passes
- Review build logs for specific errors
Enable debug mode for detailed logging:
DEBUG=1 wintool-plugin-cli validate ./my-plugin- 📖 Documentation: This guide and CLI README
- 🐛 Issues: Report bugs on GitHub
- 💬 Community: Join discussions for help and tips
- 🔍 CLI Help: Run
wintool-plugin-cli helpfor command reference
For enterprise or specialized deployments, you can create custom security policies:
{
"my-plugin": {
"maxMemoryUsage": 104857600,
"maxExecutionTime": 60000,
"allowedDomains": ["api.mycompany.com"],
"permissions": ["storage.read", "storage.write", "network.request"]
}
}For advanced development workflows, use the development server:
const PluginDevServer = require('./src/dev/plugin-dev-server');
const devServer = new PluginDevServer({
hotReload: true,
autoValidate: true,
debugMode: true
});
await devServer.start();Integrate plugin validation into your CI/CD pipeline:
# GitHub Actions example
- name: Validate Plugin
run: |
npm install -g ./cli
wintool-plugin-cli validate ./my-plugin
wintool-plugin-cli security ./my-plugin
wintool-plugin-cli test ./my-pluginHappy coding!
🔌 Build amazing plugins for WinTool! 🔌