Consider supporting user inspector plugins?
Hi @fernandojsg —
What do you think about letting developers write plugins for the inspector, by having a simple API for attaching their DOM element to a tab?
Some example use cases:
- Export current scene to glTF or OBJ
- Search for models from different model libraries
- Custom animation timeline editor, similar to frame.js
- Creating and exporting a navmesh derived from the current scene
- More detailed performance panel, explaining how many entities you have, number of materials, etc.
These all seem like interesting use cases for the inspector, but not necessarily something we'd build and support ourselves. But maybe we could have an API like this?
var el = document.createElement('div');
var btnEl = document.createElement('button');
btnEl.textContent = 'Export Scene';
btnEl.addEventListener('click', () => ...);
el.appendChild(btnEl);
AFRAME.inspector.registerPanel(el, {position: 'bottom'});
In order for plugins to not be disruptive to the current UI I would probably give them their own panel to render themselves. That panel would be invoked by the user. Idea:
- A
pluginsbutton above thescene graphview (next to the motion capture, save... buttons) that replaces thescene graphview with that of a selected plugin. The drawback would be you could not see multiple plugins or the scene graph and a plugin simultaneously.
Yeah, I think the location is not too critical unless it becomes used for a bunch of fancy stuff. Which would be a good problem to have. Could be a third, new panel that pops up from the bottom of the page as well.
Actually I started with something called modules a while back when I wanted to create a VR editor mode for the inspector. Basically you register the modules and they can be activated or deactivated. As I didn't continue with the VR editor I've just the dummy module there, but we could use it just as a starting point for something like you propose.
https://github.com/aframevr/aframe-inspector/blob/d3e0a32dfe0a9100386c1e6ec561f3003c48d526/src/lib/modules/dummy/index.js
@donmccurdy @dmarcos do you have any idea that could be simple enough to use it as an example and as a proof of concept of the plugin system?
- @tomas-polach from the Archilogic3D/3D.io team already customized the inspector for Google Blocks integration
- @ngokevin did it for motion capture integration
- I did it a while ago for saving to HTML to a remote server
I'm sure there are quite a few more examples out there so definitely eager to see this.
I think a couple of the ideas in my OP would be simple enough. Namely,
- Export scene or selected object(s) to glTF
- More detailed performance panel, explaining how many entities you have, number of materials, etc.
@donmccurdy I had already added the gltf exporter to the inspector (https://blog.mozvr.com/gltf-exporter-in-three-js-and-a-frame/) but maybe I could just remove it from the main code and use it as a simple use case for the plugin. I was working also in the inVR inspector mode as a plugin, but it didn't include any panel or UI, just a shortcut to enable/disable it.
I'm not sure how to deal with plugins that will need to include UI as they should be using react instead of plain javascript so it's not something straightforward.
Proposals for implementation welcome :)
Ok, I'll start checking the @tomas-polach as it seems that it's the only one that it's really implemented without modifying the core (https://github.com/archilogic-com/3dio-inspector-plugins)
One question, should we have types of plugins and let the editor have a specific behaviour for it? Let's say:
- tab plugin: the editor will create the tab and handle its visibility, and the plugin will include the component
- entity: it will add a button probably to the entity panel.
- component: similar but for component.
- no visibility: it could modify the viewport, or other things but it doesn't have an UI
Just some quick thoughts about it:
- pros: User don't need to deal common things like creating or managing the state of the tab, and if we modify something like CSS or so, it will still match. So plugins code will be simplified. And we'll get a better control of them.
- cons: Probably we'll end up having a use case that we don't have a type of plugin for it, what about
rawand you could just do whatever you want at your own responsability?
I had already added the gltf exporter to the inspector
oh, nice! might as well keep it in the main code then.
I'm not sure how to deal with plugins that will need to include UI as they should be using react instead of plain javascript so it's not something straightforward.
IMO that's the only important case; if plugins did not need a UI they wouldn't need to integrate with the inspector.
One question, should we have types of plugins and let the editor have a specific behaviour for it?
My vote would be, all plugins are tab plugins (or some sort of UI panel), and there should be an API so that the plugin can listen for events when the selected entity changes. But I don't think there should be separate types of plugins for entities/components/no-ui.
So really this requires
- A DOM element that won't be overwritten by React.
- Events when selected entity changes, inspector opens/closes, tab opens/closes, etc.
sry for joining late. this would be a great step forward!! all major editors thrive on community driven plugins. i'm fine with all the proposals as mentioned above and i am looking forward to adapt the 3d.io inspector plugins to fit into the new structure. based on the proposals above, here is my two cents:
Using Plugins
- could be activated and loaded on demand from separate repos in settings menu: (so that non-coders can activate it easily)

Writing Plugins
- exposing the existing
moduleinfrastructure (renamed toplugins?) - reducing required params to
initandremove - providing optional plugin methods like:
registerShortcut,registerPanel,registerMenuItemand plugin events - an example for a panel performance plugin could then look like this:
AFRAME.inspector.registerPlugin('performance-panel', {
init: function () {
// Create Plugin UI
var myPanel = document.createElement('div');
function updatePanel () {
myPanel.innerHTML = AFRAME.inspector.selected.object3D.uuid
}
// Regsiter Plugin UI
this.registerPanel(myPanel, {
type: 'tab' // might be: 'floating', 'tab', 'tab-widget' ?
});
this.registerShortcut(80, () => {
this.togglePanel();
}, false);
this.registerMenuItem('Performance Panel', () => {
this.showPanel();
}, false);
// Plugin Events
this.on('showpanel', () => {
updatePanel()
// bind inspector events when panel becomes visible
AFRAME.inspector.on('selected', updatePanel)
}, false);
this.on('hidepanel', () => {
// unbind events when panel becomes hidden
AFRAME.inspector.off('selected', updatePanel)
}, false);
},
// called once when inspetor is closing or plugin has been disabled
remove: function () {
// clean up
}
});
What's the minimum API we can introduce that would let people start experimenting with interesting plugins? I think search/discovery and keybindings might be best left until there are some examples in the wild and plugin authors can perhaps help contribute then.
Suggested:
- Fire events on scene when inspector is shown/hidden. (see inspector.js).
- Fire events when entity is selected in the LHS sidebar. (perhaps pass the inspector's own events to the scene object?)
- Hook to create a persistent DOM element in the inspector's UI. A new bottom panel, perhaps with a named tab for plugins, seems like the easiest place to start.
Another quick note, more to just keep this in one place... if my plugin needs to scan the scene, filtering the inspector's objects out is a bit of guesswork:
const content = new THREE.Scene();
this.sceneEl.object3D.updateMatrixWorld();
this.sceneEl.object3D.traverse((node) => {
if (!node.isMesh || node.name.match(/^[XYZE]+|picker$/)) return;
const clone = node.clone();
clone.matrix.copy(node.matrixWorld);
content.add(clone);
});
... another nice-to-have would be markings (userData, say?) to identify these objects, or a more predictable prefix on the object names.
Also, I'm using a helper component to get around the lack of inspector open/close events, and that's working reasonably well:
AFRAME.registerComponent('inspector-plugin-mything', {
init: function () {
const tmpEl = document.createElement('div');
tmpEl.innerHTML = panelTpl;
const panelEl = tmpEl.children[0];
document.body.appendChild(panelEl);
this.plugin = new MyPlugin(panelEl, this.el);
},
pause: function () {
this.plugin.setVisible(true);
},
play: function () {
this.plugin.setVisible(false);
}
});