Skip to content

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.

bash
# 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 dev

Open 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

PanelPurpose
Viewport3D scene view with camera controls and gizmos
HierarchyEntity tree with drag-and-drop reparenting
InspectorProperty editors for the selected entity's components
ConsoleLog output with level filtering
AssetsProject file browser with import, creation, and preview
SceneScene settings (environment, fog, shadows, post-processing)
BuildBuild configuration, execution, and log output
SettingsEditor preferences (theme, shortcuts, display)
ProfilerPer-system timing breakdown and render statistics
TimelineAnimation timeline editor
VCSGit integration with change tracking and diff display
PluginsPlugin manager with activate/deactivate controls

Layout Presets

Switch between five built-in layout presets from the View menu:

PresetDescription
DefaultHierarchy left, Viewport center, Inspector right, Console/Assets bottom
2-ColumnHierarchy + Inspector stacked left, wide Viewport right
3-ColumnHierarchy left, Viewport + Console center, Inspector right
TallViewport top, all panels in a bottom row
WideViewport 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:

typescript
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

ActionControl
OrbitMiddle-click drag or Alt + left-click drag
PanShift + middle-click drag
ZoomScroll wheel
Fly modeRight-click + WASD
Focus on selectionF key

Transform Gizmos

The viewport toolbar provides three gizmo modes:

GizmoShortcutDescription
TranslateWMove along X/Y/Z axes or planes
RotateERotate around X/Y/Z axes
ScaleRScale 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+click to add to or toggle the selection
  • Drag a rectangle in the viewport to marquee-select multiple entities
  • Ctrl+A to 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 TypeEditor
numberNumber input with scrub-to-edit
booleanCheckbox
stringText input
Vec3Three-field vector input (X, Y, Z)
ColorColor picker with hex/RGB input
EnumDropdown selector
Entity referenceEntity picker with hierarchy search
Asset referenceAsset picker with browser integration
ArrayExpandable 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:

typescript
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:

ValueBehavior
'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:

typescript
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

typescript
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:

typescript
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:

typescript
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:

typescript
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:

typescript
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.

typescript
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

ShortcutAction
Ctrl+ZUndo
Ctrl+Shift+Z / Ctrl+YRedo
Ctrl+SSave scene
Ctrl+Shift+PCommand palette
Delete / BackspaceDelete selected entities
Ctrl+DDuplicate selection
Ctrl+ASelect all
WTranslate gizmo
ERotate gizmo
RScale gizmo
FFocus on selection
Ctrl+CCopy
Ctrl+VPaste

Custom Shortcuts

Register custom keyboard shortcuts through the shortcuts extension point:

typescript
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):

typescript
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:

typescript
// 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:

typescript
import { createEffect } from 'solid-js';

createEffect(() => {
  registry.version(); // Track changes
  const panels = registry.getAll('panels');
  // Re-render panel list...
});

Next Steps

Proprietary software. All rights reserved.