3.3 KiB
Nebula Plugins (Early Preview)
This document explains how to build simple plugins for Nebula. The initial API is intentionally small and will grow with feedback.
Overview
- Plugins live under either of these folders:
- App folder:
<app>/plugins/<plugin-id>/ - User folder:
%APPDATA%/Nebula/plugins/<plugin-id>/(Windows) – preferred for user-installed plugins.
- App folder:
- Each plugin has a
plugin.jsonmanifest. Optionalmain.jsruns in the main process. Optionalrenderer-preload.jsruns in the renderer preload context and can expose safe APIs viacontextBridge. - Plugins are loaded on app start. Toggle a plugin by setting
"enabled": falsein its manifest.
Manifest (plugin.json)
Example:
{
"id": "my-plugin",
"name": "My Plugin",
"version": "0.1.0",
"description": "What it does",
"main": "main.js",
"rendererPreload": "renderer-preload.js",
"enabled": true
}
Fields:
- id: Unique id. Defaults to folder name if omitted.
- main: Optional entry for main process integration.
- rendererPreload: Optional file injected into the preload. Use it to expose limited APIs.
- enabled: Defaults to true.
Main process API (activate)
If main is present, export an activate(ctx) function. The ctx contains:
- Electron:
app,BrowserWindow,ipcMain,session,Menu,dialog,shell - paths:
{ appPath, userData, pluginDir } - log/warn/error: prefix logs with your plugin id
- on(event, cb): subscribe to lifecycle events (experimental)
- registerIPC(channel, handler): quickly expose an
ipcMain.handle - registerWebRequest(filter, listener): attach
session.webRequest.onBeforeRequest
Example:
module.exports.activate = (ctx) => { ctx.log('hello'); ctx.registerIPC('my-plugin:do', async (_evt, payload) => ({ ok: true })); ctx.registerWebRequest({ urls: [':///*'] }, (details) => ({ cancel: false })); };
Renderer preload API
If rendererPreload is present, it will be require()-d from the app preload. You can use contextBridge to expose a safe surface to the page:
const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('myPlugin', { hello: () => ipcRenderer.invoke('my-plugin:do'), });
Your exposed API will be available on window.myPlugin in renderer/ code (e.g., script.js).
Sample plugin
A working sample is included at plugins/sample-hello/:
- Adds menu item "Say Hello (Sample Plugin)" under Help.
- Exposes
window.sampleHello.ping()andwindow.sampleHello.onHello(cb).
Try it from the DevTools console:
await window.sampleHello.ping(); window.sampleHello.onHello((m) => console.log('got hello', m));
Click Help -> Say Hello (Sample Plugin) to see the message delivered to the page.
Loading order and safety
- Plugins load after the app is ready. Renderer preloads run after Nebula's own preload has exposed its APIs.
- Context isolation stays enabled. Only data explicitly exposed via
contextBridgeis available to pages. - Avoid long blocking work in plugin activation.
Debugging
- See logs with a
[Plugin:<id>]prefix in the app console. - Temporarily disable a plugin by setting
enabled: falseinplugin.json.
Roadmap
This is a first pass. Planned next:
- Enable plugin settings UI
- Hot reload/reload button
- More lifecycle hooks (tab events, context menu contributions)
- Theming hooks