Skip to main content

Event System

MistWarp provides a comprehensive event system for communication between components, addons, and extensions.

Overview

The event system enables:

  • Component-to-component communication
  • Addon integration with editor events
  • Extension lifecycle management
  • State change notifications
  • User interaction tracking

Redux Events

State Change Events

Listen for Redux state changes:

// Listen to all state changes
addon.tab.redux.addEventListener('statechanged', (event) => {
const { action, prev, next } = event.detail;
console.log('Action:', action.type);
});

// Filter specific actions
addon.tab.redux.addEventListener('statechanged', (event) => {
const { action } = event.detail;

switch (action.type) {
case 'scratch-gui/targets/SET_TARGET':
handleTargetChange(action.targetId);
break;
case 'scratch-gui/mode/SET_PLAYER':
handleModeChange(action.isPlayerOnly);
break;
}
});

Common Redux Actions

// Target (sprite/stage) selection
'scratch-gui/targets/SET_TARGET'
'scratch-gui/targets/UPDATE_TARGET_LIST'

// Project state
'scratch-gui/project-state/SET_PROJECT_TITLE'
'scratch-gui/project-state/SET_PROJECT_SAVED'

// Editor mode
'scratch-gui/mode/SET_PLAYER'
'scratch-gui/mode/SET_FULLSCREEN'

// Blocks workspace
'scratch-gui/workspace-blocks/UPDATE_BLOCKS'
'scratch-gui/workspace-blocks/SET_BLOCKS'

// Modals
'scratch-gui/modals/OPEN_MODAL'
'scratch-gui/modals/CLOSE_MODAL'

DOM Events

Editor Navigation

Track URL and navigation changes:

addon.tab.addEventListener('urlChange', (event) => {
const { oldURL, newURL } = event.detail;

if (newURL.includes('/editor')) {
console.log('Entered editor mode');
} else if (newURL.includes('/player')) {
console.log('Entered player mode');
}
});

Component Events

Listen for specific component interactions:

// Block workspace events
addon.tab.addEventListener('workspaceUpdate', (event) => {
console.log('Workspace updated:', event.detail);
});

// Stage events
addon.tab.addEventListener('stageClick', (event) => {
const { x, y } = event.detail;
console.log(`Stage clicked at: (${x}, ${y})`);
});

// Sprite list events
addon.tab.addEventListener('spriteSelected', (event) => {
console.log('Sprite selected:', event.detail.spriteId);
});

Custom Events

Dispatching Events

Create and dispatch custom events:

// Simple custom event
addon.tab.dispatchEvent(new CustomEvent('myEvent', {
detail: { data: 'example' }
}));

// Complex event with multiple data
addon.tab.dispatchEvent(new CustomEvent('projectAnalyzed', {
detail: {
spriteCount: 5,
blockCount: 127,
complexity: 'medium',
timestamp: Date.now()
}
}));

Event Listeners

Listen for custom events:

// Listen for specific custom events
addon.tab.addEventListener('projectAnalyzed', (event) => {
const { spriteCount, blockCount, complexity } = event.detail;
updateProjectStats(spriteCount, blockCount, complexity);
});

// Generic event listener
addon.tab.addEventListener('myEvent', handleMyEvent);

function handleMyEvent(event) {
console.log('Custom event received:', event.detail);
}

Extension Events

Extension Lifecycle

Track extension loading and unloading:

// Extension loaded
vm.runtime.on('EXTENSION_ADDED', (extensionId) => {
console.log('Extension loaded:', extensionId);
});

// Extension removed
vm.runtime.on('EXTENSION_REMOVED', (extensionId) => {
console.log('Extension removed:', extensionId);
});

Block Events

Monitor block execution and interactions:

// Block execution started
vm.runtime.on('BLOCK_GLOW_ON', (blockId) => {
console.log('Block executing:', blockId);
});

// Block execution ended
vm.runtime.on('BLOCK_GLOW_OFF', (blockId) => {
console.log('Block finished:', blockId);
});

// Block added to workspace
vm.runtime.on('BLOCKS_ADDED', (blockIds) => {
console.log('Blocks added:', blockIds);
});

Project Events

Project Lifecycle

Track project loading, saving, and changes:

// Project loaded
vm.runtime.on('PROJECT_LOADED', () => {
console.log('Project loaded successfully');
});

// Project saved
addon.tab.addEventListener('projectSaved', (event) => {
console.log('Project saved:', event.detail.title);
});

// Project modified
addon.tab.addEventListener('projectModified', (event) => {
console.log('Project has unsaved changes');
});

Asset Events

Monitor costume and sound changes:

// Costume added
vm.runtime.on('COSTUME_ADDED', (costumeId, targetId) => {
console.log(`Costume ${costumeId} added to ${targetId}`);
});

// Sound added
vm.runtime.on('SOUND_ADDED', (soundId, targetId) => {
console.log(`Sound ${soundId} added to ${targetId}`);
});

Performance Events

Monitoring Performance

Track runtime performance metrics:

// Frame rate changes
vm.runtime.on('FRAMERATE_CHANGED', (fps) => {
console.log('FPS:', fps);
});

// Compile events
vm.runtime.on('COMPILE_START', () => {
console.log('Compilation started');
});

vm.runtime.on('COMPILE_END', (success, errors) => {
if (success) {
console.log('Compilation successful');
} else {
console.error('Compilation errors:', errors);
}
});

Event Best Practices

Memory Management

Properly clean up event listeners:

export default async function ({ addon }) {
const handleStateChange = (event) => {
// Handle event
};

// Add listener
addon.tab.redux.addEventListener('statechanged', handleStateChange);

// Clean up when addon disabled
addon.onDisabled = () => {
addon.tab.redux.removeEventListener('statechanged', handleStateChange);
};
}

Event Filtering

Filter events to avoid performance issues:

let lastUpdate = 0;
const THROTTLE_MS = 100;

addon.tab.redux.addEventListener('statechanged', (event) => {
const now = Date.now();
if (now - lastUpdate < THROTTLE_MS) {
return; // Skip this update
}
lastUpdate = now;

// Handle the event
handleStateChange(event);
});

Error Handling

Handle errors in event listeners gracefully:

addon.tab.addEventListener('myEvent', (event) => {
try {
processEvent(event.detail);
} catch (error) {
console.error('Error processing event:', error);
// Fallback behavior
}
});

Event Documentation

Creating Event Documentation

Document custom events for other developers:

/**
* Dispatched when project analysis is complete
* @event projectAnalyzed
* @type {CustomEvent}
* @property {Object} detail - Event data
* @property {number} detail.spriteCount - Number of sprites
* @property {number} detail.blockCount - Total blocks
* @property {string} detail.complexity - Project complexity level
*/