Selene Plugin SDK
Build plugins for the Selene browser without modifying the browser source code.
Quick Start
- Create a folder in
~/.selene/plugins/my-plugin/ - Add a
plugin.jsonmanifest - Add a
main.jsscript - Restart Selene — your plugin auto-loads
plugin.json
{
"name": "My Awesome Plugin",
"version": "1.0.0",
"description": "Does cool stuff on YouTube",
"author": "YourName",
"main": "main.js",
"enabled": true,
"match": [
"https://*.youtube.com/*",
"https://youtube.com/*"
]
}
Fields
| Field | Required | Default | Description |
|---|---|---|---|
name | yes | — | Plugin display name |
version | no | "1.0.0" | Semver version string |
description | no | "" | Short description |
author | no | "" | Author name |
main | no | "main.js" | Entry JS file |
enabled | no | true | Toggle plugin on/off |
match | yes | [] | URL patterns (glob) |
URL Pattern Matching
Patterns use * as wildcard:
*://*.google.com/* → all Google subdomains, any protocol
https://youtube.com/* → only HTTPS YouTube
*://*/* → every page
main.js
Your script runs in the page's Main World (same context as the website). You have access to the full DOM and the SelenePlugin API.
Storage (persistent, scoped per plugin)
SelenePlugin.storage.set('key', 'value');
const val = SelenePlugin.storage.get('key');
SelenePlugin.storage.remove('key');
const allKeys = SelenePlugin.storage.keys();
DOM Utilities
// Run code when DOM is ready
SelenePlugin.dom.ready(function() {
// your code here
});
// Inject CSS
SelenePlugin.dom.addStyle('body { background: #000 !important; }');
// Inject external script
SelenePlugin.dom.injectScript('https://example.com/lib.js');
// Wait for element to appear
SelenePlugin.dom.waitFor('.video-player', function(el) {
console.log('Player found!', el);
});
HTTP
// GET text
SelenePlugin.http.get('https://api.example.com/data', function(err, text) {
if (err) return;
console.log(text);
});
// GET JSON
SelenePlugin.http.getJSON('https://api.example.com/data', function(err, data) {
if (err) return;
console.log(data);
});
Page Info
const url = SelenePlugin.page.url; // full URL
const domain = SelenePlugin.page.domain; // hostname
const title = SelenePlugin.page.title; // page title
Logging
SelenePlugin.log('Hello!');
SelenePlugin.warn('Warning!');
SelenePlugin.error('Error!');
Example Plugin: Dark Mode for YouTube
plugin.json
{
"name": "YouTube Dark Mode",
"version": "1.0.0",
"description": "Forces dark mode on YouTube",
"author": "Selene",
"main": "main.js",
"match": ["https://*.youtube.com/*"]
}
main.js
SelenePlugin.dom.ready(function() {
SelenePlugin.dom.addStyle('ytd-app { background: #0f0f0f !important; }');
SelenePlugin.log('Dark mode applied');
});
Example Plugin: Auto-Skip Ads
plugin.json
{
"name": "Auto Skip Ads",
"version": "1.0.0",
"description": "Auto-clicks skip button on supported sites",
"match": ["https://*.youtube.com/*"]
}
main.js
SelenePlugin.dom.ready(function() {
setInterval(function() {
var skipBtn = document.querySelector('.ytp-ad-skip-button');
if (skipBtn) {
skipBtn.click();
SelenePlugin.log('Skipped ad');
}
}, 1000);
});
Plugin Distribution
Upload your plugin to the Selene Plugin Store via the browser UI (Menu → Plugins → Upload) or via the API:
POST https://rl-dev.de/api/browser/plugins
Users can then browse and download plugins from the Plugin Store dialog.
Tips
- Each plugin runs in an IIFE — your variables are scoped
- Errors are caught and logged to the browser console
localStorageis shared with the page; useSelenePlugin.storagefor plugin-scoped data- Plugins are re-injected on every page navigation
- The
matchpatterns are case-insensitive