Editor Guides
This guide covers practical workflows for the Web Engine Dev visual editor -- from basic scene editing to writing custom plugins. For the architectural overview, see Editor Overview. For the full extension point reference, see Editor Plugins.
Setting Up the Editor
Prerequisites
- Node.js 18+ and pnpm installed
- A cloned copy of the monorepo with dependencies installed (
pnpm install)
Starting the Editor
The editor consists of two processes: a backend server and the frontend application.
# Terminal 1: Start the editor server (filesystem access, asset importing, git)
pnpm --filter editor-server dev
# Terminal 2: Start the editor frontend (SolidJS UI)
pnpm --filter editor devOpen the URL printed by the dev server (typically http://localhost:5173) in a browser that supports WebGPU (Chrome 113+, Edge 113+, Safari 18+, or Firefox Nightly with flags).
Workspace Layout
The editor uses a dockable panel system powered by dockview-core. Panels can be dragged, resized, tabbed, and rearranged freely.
Default Layout
+------------+----------------------+------------+
| Hierarchy | Viewport | Inspector |
| | | |
| | | |
+------------+----------------------+------------+
| Console / Assets (tabbed) |
+------------------------------------------------+Built-in Panels
| Panel | Purpose |
|---|---|
| Viewport | 3D scene view with camera controls and gizmos |
| Hierarchy | Entity tree with drag-and-drop reparenting |
| Inspector | Property editors for the selected entity's components |
| Console | Log output with level filtering |
| Assets | Project file browser with import, creation, and preview |
| Scene | Scene settings (environment, fog, shadows, post-processing) |
| Build | Build configuration, execution, and log output |
| Settings | Editor preferences (theme, shortcuts, display) |
| Profiler | Per-system timing breakdown and render statistics |
| Timeline | Animation timeline editor |
| VCS | Git integration with change tracking and diff display |
| Plugins | Plugin manager with activate/deactivate controls |
Layout Presets
Switch between five built-in layout presets from the View menu:
| Preset | Description |
|---|---|
| Default | Hierarchy left, Viewport center, Inspector right, Console/Assets bottom |
| 2-Column | Hierarchy + Inspector stacked left, wide Viewport right |
| 3-Column | Hierarchy left, Viewport + Console center, Inspector right |
| Tall | Viewport top, all panels in a bottom row |
| Wide | Viewport full-width top, all panels in a bottom strip |
Layouts are persisted through the SessionManager and restored on next launch.
Creating Entities and Adding Components
From the Hierarchy Panel
Right-click in the Hierarchy panel to open the context menu. Select Create Entity to spawn a new empty entity. The entity appears in the tree and is automatically selected so you can add components in the Inspector.
Adding Components
With an entity selected, the Inspector panel shows its components. Use the Add Component button at the bottom of the Inspector to open the component catalog. Search or browse available component types and click to attach one.
All entity mutations go through the command/transaction system, so every action is undoable with Ctrl+Z.
Programmatic Entity Creation
For scripting and plugin development, use the command system:
import {
type CreateEntityCommand,
type AddComponentCommand,
HistoryManager,
EditorCommandBuffer,
} from '@web-engine-dev/editor-core';
// Entity creation goes through the command buffer for undo support
const createCmd: CreateEntityCommand = {
type: 'CreateEntity',
description: 'Create player entity',
execute(world) {
const entity = world.spawn();
// Store entity ID for undo
this.entityId = entity;
},
undo(world) {
world.despawn(this.entityId);
},
};
buffer.execute(createCmd, world);
history.push(createCmd);Transforming Objects in the Viewport
Camera Controls
| Action | Control |
|---|---|
| Orbit | Middle-click drag or Alt + left-click drag |
| Pan | Shift + middle-click drag |
| Zoom | Scroll wheel |
| Fly mode | Right-click + WASD |
| Focus on selection | F key |
Transform Gizmos
The viewport toolbar provides three gizmo modes:
| Gizmo | Shortcut | Description |
|---|---|---|
| Translate | W | Move along X/Y/Z axes or planes |
| Rotate | E | Rotate around X/Y/Z axes |
| Scale | R | Scale along X/Y/Z axes or uniformly |
Click and drag on gizmo handles to transform the selected entity. Hold Shift while dragging to snap to grid increments.
Toggle between local and world coordinate space with the space bar or the toolbar toggle.
Selection
- Left-click an entity in the viewport or hierarchy to select it
Ctrl+clickto add to or toggle the selection- Drag a rectangle in the viewport to marquee-select multiple entities
Ctrl+Ato select all entities
Using the Inspector
The Inspector panel displays property editors for every component on the selected entity. Each component section is collapsible.
Property Types
The Inspector auto-generates editors based on component reflection data:
| Property Type | Editor |
|---|---|
number | Number input with scrub-to-edit |
boolean | Checkbox |
string | Text input |
Vec3 | Three-field vector input (X, Y, Z) |
Color | Color picker with hex/RGB input |
Enum | Dropdown selector |
Entity reference | Entity picker with hierarchy search |
Asset reference | Asset picker with browser integration |
Array | Expandable list with add/remove |
Multi-Entity Editing
When multiple entities are selected, the Inspector shows properties common to all selected entities. Editing a property applies the change to every selected entity as a single undo step.
Component Header Menu
Right-click a component header in the Inspector for options:
- Copy Component -- Copy component data to clipboard
- Paste Component -- Paste component data from clipboard
- Reset to Default -- Reset all properties to their default values
- Remove Component -- Remove the component from the entity
Asset Browser
The Assets panel provides a file browser for project assets.
Importing Assets
Drag files from your operating system onto the Assets panel, or click the Import button. The editor server processes imports and generates thumbnails. Supported file types include:
- Models:
.glb,.gltf - Images:
.png,.jpg,.webp,.hdr - Audio:
.mp3,.ogg,.wav - Scripts:
.ts,.js - Scenes:
.scene - Prefabs:
.prefab
Creating Assets
Right-click in the Assets panel to create new assets:
- Script -- New TypeScript script with a template
- Material -- New PBR material definition
- Prefab -- Save selected entity hierarchy as a reusable prefab
- Scene -- New empty scene file
- Folder -- New directory for organization
Asset Preview
Select an asset to see a preview in the Asset Preview panel. For 3D models, the preview renders the model with orbit controls. For materials, it shows a preview sphere.
Drag and Drop
Drag assets from the browser into the viewport or hierarchy to instantiate them. Models are spawned as entities with the appropriate rendering components. Scripts are attached as script components.
Creating Custom Panels
PanelDefinition
Register custom panels using the PanelRegistry:
import { registerPanel } from '../layout/PanelRegistry';
import type { Component } from 'solid-js';
// Define the panel's SolidJS component
const MyCustomPanel: Component<{ params?: Record<string, unknown> }> = () => {
return <div class="my-panel">
<h3>Custom Panel Content</h3>
<p>This panel was registered by a plugin.</p>
</div>;
};
// Register the panel
registerPanel({
id: 'my-custom-panel',
title: 'My Panel',
component: MyCustomPanel,
selectionBehavior: 'preserve', // 'clear' | 'preserve' | 'custom'
alwaysRender: false, // Keep alive when hidden (for canvas panels)
});Panel Selection Behavior
The selectionBehavior property controls what happens when the user clicks the panel background:
| Value | Behavior |
|---|---|
'clear' | Clicking background clears entity/asset selection (default) |
'preserve' | Clicks never affect selection (for Inspector, Asset Preview, Viewport) |
'custom' | Panel handles selection logic internally |
Registering via ExtensionRegistry
For plugin-based panel registration, use the ExtensionRegistry:
import { type ExtensionRegistry } from '@web-engine-dev/editor-core';
function registerMyPanel(registry: ExtensionRegistry): () => void {
return registry.register('panels', {
id: 'analytics',
title: 'Analytics Dashboard',
defaultPosition: 'bottom',
create: (container: HTMLElement) => {
container.textContent = 'Analytics dashboard content';
return {
dispose: () => { container.textContent = ''; },
};
},
}, 'my-plugin-id');
}Writing Editor Plugins
Plugin Structure
An editor plugin registers contributions at well-known extension points. The ExtensionRegistry supports 15 extension points including panels, toolbar, commands, shortcuts, gizmos, contextMenus, themes, buildHooks, and more.
Minimal Plugin
import type { ExtensionRegistry } from '@web-engine-dev/editor-core';
export function activateMyPlugin(registry: ExtensionRegistry): () => void {
const disposers: Array<() => void> = [];
// Register a command
disposers.push(
registry.register('commands', {
commands: [{
id: 'myPlugin.greet',
title: 'Greet User',
category: 'My Plugin',
execute: () => {
console.log('Hello from my plugin!');
},
}],
}, 'my-plugin'),
);
// Register a toolbar button
disposers.push(
registry.register('toolbar', {
id: 'myPlugin.greetButton',
label: 'Greet',
icon: 'wand',
section: 'tools',
execute: () => {
console.log('Toolbar button clicked!');
},
isEnabled: () => true,
}, 'my-plugin'),
);
// Register a keyboard shortcut
disposers.push(
registry.register('shortcuts', {
shortcuts: [{
id: 'myPlugin.greetShortcut',
keys: 'Ctrl+Shift+G',
command: 'myPlugin.greet',
label: 'Greet User',
}],
}, 'my-plugin'),
);
// Cleanup function removes all contributions
return () => {
for (const dispose of disposers) dispose();
};
}Plugin Manifest
For formally managed plugins, define a manifest:
import type { PluginManifest } from '@web-engine-dev/editor-core';
const manifest: PluginManifest = {
id: 'my-plugin',
name: 'My Plugin',
version: '1.0.0',
description: 'Adds custom tools to the editor',
author: 'Your Name',
capabilities: {
panels: true,
toolbar: true,
menus: true,
},
configSchema: {
properties: {
greeting: {
type: 'string',
default: 'Hello',
description: 'The greeting message to display',
},
autoGreet: {
type: 'boolean',
default: false,
description: 'Automatically greet on startup',
},
},
},
};Plugin states flow through: unloaded -> loaded -> activated (or degraded / error). The Plugin Manager panel shows all plugins with their current state and provides activate/deactivate controls.
Custom Component Editor
Register a custom property editor for a specific component type:
registry.register('componentEditors', {
componentId: MyComponentTypeId,
priority: 10,
create: (container, props) => {
// props.entityId, props.componentId, props.world
container.textContent = 'Custom editor for MyComponent';
return {
dispose: () => { container.textContent = ''; },
};
},
}, 'my-plugin');Context Menu Items
Add items to context menus in specific panels:
registry.register('contextMenus', {
location: 'hierarchy',
items: [{
id: 'myPlugin.duplicateSpecial',
label: 'Duplicate with Children',
icon: 'copy',
action: () => {
// Custom duplicate logic
},
when: 'selectionCount > 0',
}],
}, 'my-plugin');Build Hooks
Hook into the build pipeline for pre-build and post-build processing:
registry.register('buildHooks', {
preBuild: (context) => {
console.log(`Building profile: ${context.profileId}`);
console.log(`Target: ${context.target}`);
// Modify context.config before build starts
},
postBuild: (context, result) => {
console.log(`Build ${result.success ? 'succeeded' : 'failed'}`);
console.log(`Duration: ${result.totalDurationMs}ms`);
console.log(`Output size: ${result.totalSize} bytes`);
},
}, 'my-plugin');Keyboard Shortcuts and the Command Palette
Command Palette
Open the command palette with Ctrl+Shift+P (or Cmd+Shift+P on macOS). Type to fuzzy-search through all registered commands. Commands are registered via the CommandPaletteRegistry or through the commands extension point.
import { CommandPaletteRegistry } from '@web-engine-dev/editor-core';
const palette = new CommandPaletteRegistry();
palette.register({
id: 'editor.toggleGrid',
label: 'Toggle Grid',
category: 'View',
shortcut: 'Ctrl+G',
keywords: ['grid', 'overlay', 'viewport'],
run: () => {
// Toggle grid visibility
},
enabled: () => true,
});Default Keyboard Shortcuts
| Shortcut | Action |
|---|---|
Ctrl+Z | Undo |
Ctrl+Shift+Z / Ctrl+Y | Redo |
Ctrl+S | Save scene |
Ctrl+Shift+P | Command palette |
Delete / Backspace | Delete selected entities |
Ctrl+D | Duplicate selection |
Ctrl+A | Select all |
W | Translate gizmo |
E | Rotate gizmo |
R | Scale gizmo |
F | Focus on selection |
Ctrl+C | Copy |
Ctrl+V | Paste |
Custom Shortcuts
Register custom keyboard shortcuts through the shortcuts extension point:
registry.register('shortcuts', {
shortcuts: [
{
id: 'myPlugin.action1',
keys: 'Ctrl+Shift+1',
command: 'myPlugin.action1',
when: 'editorFocus',
label: 'My Custom Action',
},
],
}, 'my-plugin');The when field accepts context key expressions (similar to VS Code) for conditional activation. The ContextKeyService from editor-core evaluates these expressions.
Build System
Running a Build
Open the Build panel from the View menu or use Ctrl+Shift+B. The panel provides:
- Profile selection -- Choose a build profile (development, production, etc.)
- Target platform -- Select the output target
- Build button -- Start the build process
- Progress display -- Stage progress with log output
- Output report -- File sizes and optimization details
Build Service API
For programmatic builds (CI/CD, plugins, scripts):
import { BuildService } from '@web-engine-dev/editor-core';
const buildService = new BuildService(config);
// Subscribe to build events
buildService.onStageChange((stage) => {
console.log(`Stage: ${stage.name} (${stage.progress}%)`);
});
buildService.onLog((entry) => {
console.log(`[${entry.level}] ${entry.message}`);
});
// Start a build
const result = await buildService.build({
profileId: 'production',
target: 'web',
outputDir: './dist',
development: false,
});
console.log(`Build ${result.success ? 'succeeded' : 'failed'}`);
console.log(`Total size: ${(result.totalSize / 1024).toFixed(1)} KB`);Build Hooks from Plugins
Plugins can hook into the build pipeline through the buildHooks extension point to modify configuration before builds and process results after builds. See the Writing Editor Plugins section above.
Querying Extensions at Runtime
Use getAll() on the ExtensionRegistry to discover all contributions at any extension point:
// Get all registered panels
const panels = registry.getAll('panels');
// Get all toolbar actions
const toolbarItems = registry.getAll('toolbar');
// Get all commands for the palette
const commands = registry.getAll('commands');
// Get all contributions from a specific plugin
const pluginExtensions = registry.getByPlugin('my-plugin');The registry's version signal increments on every registration change, enabling reactive UI updates in SolidJS:
import { createEffect } from 'solid-js';
createEffect(() => {
registry.version(); // Track changes
const panels = registry.getAll('panels');
// Re-render panel list...
});Next Steps
- Editor Overview -- Architecture and feature tour
- Editor Plugins -- Full extension point reference with all 15 contribution types
- Editor Panels -- Panel system details
- ECS -- Understanding entities, components, and systems