blockbench-plugins

Blockbench Plugin Development

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "blockbench-plugins" with this command: npx skills add jasonjgardner/blockbench-mcp-project/jasonjgardner-blockbench-mcp-project-blockbench-plugins

Blockbench Plugin Development

Overview

Blockbench runs on Electron (desktop) and as a web PWA, using THREE.js for 3D rendering and Vue 2 for reactive UI. Plugins have full access to global APIs within an isolated execution context.

Quick Reference

Task Approach

Create clickable command new Action()

  • add to menus/toolbars

Show form/dialog new Dialog() with form fields

Add sidebar panel new Panel() with Vue component

Modify model elements Use Undo.initEdit() → modify → Undo.finishEdit()

Custom import/export new Codec()

  • new ModelFormat()

React to changes Blockbench.on('event_name', callback)

Plugin File Structure

plugins/ └── my_plugin/ ├── my_plugin.js # Main file (required, ID must match filename) ├── about.md # Extended docs (optional) └── icon.png # 48x48 icon (optional)

Plugin Registration Template

(function() { // Store references for cleanup let myAction, myPanel, myDialog, eventCallback;

Plugin.register('my_plugin', {
    title: 'My Plugin',
    author: 'Author Name',
    description: 'Short description',
    icon: 'extension',            // Material icon name
    version: '1.0.0',
    variant: 'both',              // 'desktop', 'web', or 'both'
    min_version: '4.8.0',
    tags: ['Utility'],
    
    onload() {
        // Initialize all components here
    },
    
    onunload() {
        // CRITICAL: Delete ALL components here
    },
    
    oninstall() {
        Blockbench.showQuickMessage('Installed!');
    }
});

})();

Actions

Actions are clickable commands for menus, toolbars, and keybindings.

myAction = new Action('my_action_id', { name: 'Action Name', description: 'Tooltip text', icon: 'star', category: 'edit', // For keybind settings

condition: () => Cube.selected.length > 0,

keybind: new Keybind({ key: 'k', ctrl: true }),

click(event) {
    // Action logic
}

});

MenuBar.addAction(myAction, 'filter'); // Add to Filter menu

// Cleanup myAction.delete();

Menu locations: 'file' , 'edit' , 'transform' , 'filter' , 'tools' , 'view' , 'help'

Action variants:

// Toggle (on/off state) new Toggle('toggle_id', { name: 'Feature', default: false, onChange(value) { /* handle */ } });

// Tool (viewport interaction) new Tool('tool_id', { name: 'My Tool', cursor: 'crosshair', onCanvasClick(data) { /* handle / }, onCanvasDrag(data) { / handle */ } });

Dialogs

myDialog = new Dialog({ id: 'my_dialog', title: 'Dialog Title', width: 540,

form: {
    name: { label: 'Name', type: 'text', value: 'default' },
    count: { label: 'Count', type: 'number', value: 10, min: 1, max: 100 },
    enabled: { label: 'Enabled', type: 'checkbox', value: true },
    mode: {
        label: 'Mode',
        type: 'select',
        options: { a: 'Option A', b: 'Option B' },
        value: 'a'
    },
    color: { label: 'Color', type: 'color', value: '#ff0000' },
    // Conditional field
    advanced: {
        label: 'Advanced',
        type: 'text',
        condition: (form) => form.enabled
    }
},

onConfirm(formData) {
    console.log(formData.name, formData.count);
    this.hide();
}

});

myDialog.show();

// Quick dialogs Blockbench.textPrompt('Enter Value', 'default', (text) => { }); Blockbench.showMessageBox({ title: 'Alert', message: 'Text', buttons: ['OK'] });

Panels

Panels appear in sidebars with Vue components.

myPanel = new Panel('my_panel', { name: 'My Panel', icon: 'dashboard', condition: () => Format.animation_mode,

default_position: {
    slot: 'left_bar',             // 'left_bar', 'right_bar', 'bottom'
    height: 300
},

component: {
    template: `
        <div>
            <h3>{{ title }}</h3>
            <ul><li v-for="item in items">{{ item.name }}</li></ul>
            <button @click="refresh">Refresh</button>
        </div>
    `,
    data() {
        return { title: 'Items', items: [] };
    },
    methods: {
        refresh() {
            this.items = Cube.selected.map(c => ({ name: c.name }));
        }
    }
}

});

// Cleanup myPanel.delete();

Model Manipulation (with Undo)

CRITICAL: Always wrap modifications in Undo for user reversibility.

// Start tracking Undo.initEdit({ elements: Cube.selected });

// Modify elements Cube.selected.forEach(cube => { cube.from[0] += 5; cube.to[1] = 20; cube.rotation[1] = 45; });

// Update view Canvas.updateView({ elements: Cube.selected, element_aspects: { geometry: true, transform: true } });

// Commit Undo.finishEdit('Move cubes');

Creating Elements

// Cube let cube = new Cube({ name: 'my_cube', from: [0, 0, 0], to: [16, 16, 16], origin: [8, 8, 8], rotation: [0, 45, 0] }).init(); cube.addTo(Group.selected[0]); // Add to group

// Group (bone) let group = new Group({ name: 'bone_arm', origin: [0, 12, 0] }).init(); group.addTo(); // Add to root

// Texture let texture = new Texture({ name: 'my_texture' }); texture.fromPath('/path/to/file.png'); // or .fromDataURL() texture.add(true); // true = add to undo

Global Collections

Collection Description

Cube.all / Cube.selected

All cubes / selected cubes

Group.all / Group.selected

All groups / selected groups

Mesh.all / Mesh.selected

All meshes / selected meshes

Texture.all / Texture.selected

All textures / selected

Animation.all / Animation.selected

All animations / selected

Outliner.elements

All outliner elements

Event System

// Subscribe eventCallback = (data) => { /* handle */ }; Blockbench.on('update_selection', eventCallback);

// Unsubscribe (use SAME function reference) Blockbench.removeListener('update_selection', eventCallback);

Common events: update_selection , select_project , new_project , load_project , save_project , close_project , add_cube , add_group , add_texture , add_animation , select_animation , render_frame , undo , redo , finish_edit

See references/events.md for full list.

Custom Menus

let menu = new Menu([ 'existing_action_id', myAction, '_', // Separator { name: 'Custom Item', icon: 'star', click() { /* handle / } }, { name: 'Submenu', children: [ / more items */ ] } ]);

menu.open(event); // Open at mouse position

Format and Codec (Import/Export)

const myCodec = new Codec('my_codec', { name: 'My Format', extension: 'mymodel',

compile(options) {
    // Model → file content
    let data = { bones: [] };
    Group.all.forEach(g => {
        data.bones.push({
            name: g.name,
            pivot: g.origin,
            cubes: g.children.filter(c => c instanceof Cube).map(c => ({
                from: c.from, to: c.to
            }))
        });
    });
    return JSON.stringify(data, null, 2);
},

parse(content, path) {
    // File content → model
    let data = JSON.parse(content);
    newProject(myFormat);
    data.bones.forEach(b => {
        let group = new Group({ name: b.name, origin: b.pivot }).init();
        b.cubes.forEach(c => {
            new Cube({ from: c.from, to: c.to }).init().addTo(group);
        });
    });
    Canvas.updateAll();
}

});

const myFormat = new ModelFormat('my_format', { id: 'my_format', name: 'My Format', icon: 'icon_name', codec: myCodec, box_uv: true, bone_rig: true, animation_mode: true });

// Cleanup myFormat.delete(); myCodec.delete();

Condition Patterns

// Function condition: () => Format.id === 'bedrock' && Cube.selected.length > 0

// Object (combined with AND) condition: { formats: ['bedrock', 'java_block'], modes: ['edit', 'paint'], project: true, selected: { cube: 1 }, method: () => customCheck() }

Code Style Conventions

Type Convention Examples

Classes PascalCase OutlinerElement , Animation

Methods camelCase updateTransform , getSelectedFaces

Properties snake_case data_points , uv_offset

Event/Action IDs snake_case select_project , my_action

Critical Cleanup Pattern

MUST delete all components in onunload() to prevent memory leaks.

let action, panel, dialog, toolbar, format, codec, eventCallback, css;

Plugin.register('my_plugin', { onload() { action = new Action('id', { /* ... / }); panel = new Panel('id', { / ... / }); format = new ModelFormat('id', { / ... / }); codec = new Codec('id', { / ... */ });

    eventCallback = (data) => { };
    Blockbench.on('update_selection', eventCallback);
    
    css = Blockbench.addCSS('.my-class { color: blue; }');
},

onunload() {
    action.delete();
    panel.delete();
    format.delete();
    codec.delete();
    Blockbench.removeListener('update_selection', eventCallback);
    css.delete();
}

});

Anti-Patterns

// ❌ No stored reference = memory leak onload() { new Action('leaked', { }); }

// ❌ No Undo = users can't reverse Cube.selected.forEach(c => c.from[0] += 1);

// ❌ Bundling THREE.js (already global) import * as THREE from 'three';

// ❌ Global pollution window.myData = {};

Built-in Libraries (Do Not Bundle)

  • THREE — Three.js 3D rendering

  • Vue — Vue 2 reactive UI

  • JSZip — ZIP handling

  • marked — Markdown parsing

  • Molang — Molang expression parser

Icons

icon: 'star' // Material icon icon: 'fa-bone' // Font Awesome icon: 'icon-player' // Blockbench custom icon: 'data:image/...' // Base64 image

TypeScript Setup

npm i --save-dev blockbench-types

/// <reference types="blockbench-types" />

Template Files

  • assets/plugin_template.js — Complete starter plugin

  • assets/format_plugin.js — Custom format/codec example

References

  • references/events.md — Full event list

  • references/api.md — Detailed API reference

  • references/elements.md — Element types and properties

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

blockbench-modeling

No summary provided by upstream source.

Repository SourceNeeds Review
General

blockbench-texturing

No summary provided by upstream source.

Repository SourceNeeds Review
General

blockbench-animation

No summary provided by upstream source.

Repository SourceNeeds Review
General

blockbench-mcp-overview

No summary provided by upstream source.

Repository SourceNeeds Review