Editor Plugins
Overview
Editor plugins extend the Zephyr3d Editor itself. A plugin can add:
- Main menu items and context menu items
- Toolbar buttons
- Custom edit tools
- Custom property accessors
- Plugin settings and persistent state
The current UI entry is Project -> Plugin Manager.... Despite the name, this panel manages externally installed editor plugins stored in the editor's global database and shared by all local projects in the same browser profile.
Installing Plugins
Open Project -> Plugin Manager..., then use one of the following actions:
Install...to install a packaged plugin from a.zipfileInstall Folder...to install an unpacked plugin folderNew Template...to generate a starter plugin package inside the editor
After a plugin is installed you can:
- Enable or disable it with the checkbox in the plugin list
- Open
Browse Files...to inspect or edit plugin files - Open
Install Package...to add a third-party npm dependency - Open
Settings...if the plugin exposes a settings schema - Remove the plugin from the editor
Package Layout
The recommended format is a multi-file plugin package with a plugin.json manifest at the package root:
my-editor-plugin/
plugin.json
index.ts
icons/
tool.svg
utils/
commands.tsExample plugin.json:
{
"id": "com.example.demo-plugin",
"name": "Demo Plugin",
"version": "0.1.0",
"description": "Example editor plugin for Zephyr3d.",
"entry": "index.ts"
}Manifest fields:
id: required and must be uniqueentry: required, relative path to the plugin entry modulename,version,description: optional but recommendeddependencies: optional third-party packages used by the plugin
Minimal Plugin
Import plugin types from @zephyr3d/editor/editor-plugin and export a default plugin definition:
import type { EditorPluginDefinition } from '@zephyr3d/editor/editor-plugin';
import { SceneNode } from '@zephyr3d/scene';
const plugin: EditorPluginDefinition = {
activate(ctx) {
ctx.registerMenuItems({
location: 'main',
items: [
{
id: 'com.example.demo-plugin.menu',
label: 'Demo Plugin',
subMenus: [
{
id: 'com.example.demo-plugin.about',
label: 'About...',
action: async () => {
await ctx.ui.message('Demo Plugin', 'The plugin is active.');
}
}
]
}
]
});
ctx.registerMenuItems({
location: 'scene-hierarchy',
items: (menuCtx) => [
{
id: 'com.example.demo-plugin.add-empty-child',
label: 'Add Empty Child',
visible: () => menuCtx.target instanceof SceneNode,
action: async () => {
if (!(menuCtx.target instanceof SceneNode) || !menuCtx.scene) {
return;
}
await menuCtx.scene.commands.addChildNode(menuCtx.target, SceneNode);
menuCtx.scene.refreshProperties();
menuCtx.scene.notifySceneChanged();
}
}
]
});
}
};
export default plugin;Plugin API
EditorPluginContext is the main entry point passed to activate(ctx).
Common capabilities:
ctx.project: read and write project files, create directories, open code filesctx.system: save plugin-global state and settingsctx.ui: show messages, confirmations, and project file/folder pickersctx.registerMenuItems(...): contribute menu itemsctx.registerToolbarItem(...): contribute toolbar buttonsctx.registerEditTool(...): register custom scene edit toolsctx.registerPropertyAccessors(...): extend the property panelctx.on(...): listen to editor events such as scene open, selection change, and node updatesctx.log(...): write plugin logs
Menu contributions receive an EditorMenuContext with:
scene: available in scene-related menusassets: available in asset browser menustarget: the clicked node, file, or other target object
When you mutate scene data directly, call:
ctx.refreshProperties()ctx.notifySceneChanged()
Scene Commands
Scene-related operations should go through EditorSceneContext.commands.
Available helpers include:
addChildNode()addShapeNode()instantiatePrefab()deleteNode()reparentNode()cloneNode()executeCommand()executeUserCallback()
This API is available from scene-aware contexts such as menuCtx.scene and editCtx.scene.
Example:
await menuCtx.scene.commands.executeCommand(new MyCustomCommand(...));For quick undoable operations without a dedicated command class:
await menuCtx.scene.commands.executeUserCallback(
async () => {
target.visible = false;
},
async () => {
target.visible = true;
}
);Settings And State
Plugins can declare a settings schema on the definition:
const plugin: EditorPluginDefinition = {
settings: {
endpoint: {
type: 'string',
label: 'API Endpoint',
description: 'Base URL used by the plugin.'
},
autoSync: {
type: 'boolean',
label: 'Auto Sync',
default: true
}
},
activate(ctx) {
// ...
}
};At runtime:
ctx.system.getSettings()/ctx.system.saveSettings()store plugin-global settingsctx.system.getState()/ctx.system.saveState()store plugin-global state datactx.project.getSettings()/ctx.project.saveSettings()store project-specific data
Use system settings for values shared across projects, and project settings for values that belong to the current project.
Third-Party Packages
If a plugin needs an npm package:
- Open
Project -> Plugin Manager... - Select the plugin
- Click
Install Package... - Enter a package spec such as
nanoidornanoid@5
After installation you can import the package directly:
import { nanoid } from 'nanoid';Installed package versions are tracked in plugin.json under dependencies.