Skip to main content

Blocks Component

The Blocks component is the central workspace where users create and edit their Scratch scripts. In MistWarp, this component has been enhanced with improved performance, additional features, and better developer tools.

Component Overview

Core Functionality

The Blocks component provides:

  • Visual Programming Interface: Drag-and-drop block editing
  • Syntax Highlighting: Color-coded block categories
  • Auto-completion: Smart block suggestions
  • Error Detection: Real-time script validation
  • Performance Optimization: Efficient rendering and updates

MistWarp Enhancements

  • Turbo Mode Integration: Visual indicators for high-performance blocks
  • Advanced Block Types: Custom JavaScript and extension blocks
  • Improved Search: Enhanced block palette search functionality
  • Custom Themes: Theming support for blocks workspace
  • Debugging Tools: Integrated debugging and profiling features

Architecture

Component Structure

// Simplified component structure
const BlocksComponent = () => {
return (
<div className="blocks-wrapper">
<BlocksPalette />
<BlocksWorkspace />
<BlocksToolbox />
</div>
);
};

Key Subcomponents

BlocksPalette

Displays available blocks organized by category:

const BlocksPalette = () => {
const categories = [
'motion', 'looks', 'sound', 'events',
'control', 'sensing', 'operators',
'variables', 'myBlocks'
];

return (
<div className="blocks-palette">
{categories.map(category => (
<CategorySection key={category} category={category} />
))}
</div>
);
};

BlocksWorkspace

The main editing area where scripts are constructed:

const BlocksWorkspace = () => {
const { vm, isRtl, options } = useBlocksWorkspace();

useEffect(() => {
initializeWorkspace(vm, isRtl, options);
}, [vm, isRtl, options]);

return <div id="blocks-workspace" />;
};

Block Categories

Standard Categories

Motion Blocks

// Motion block definitions
const motionBlocks = {
'motion_movesteps': {
message0: 'move %1 steps',
args0: [{ type: 'input_value', name: 'STEPS' }],
category: 'motion',
colour: '#4C97FF'
}
// ... more motion blocks
};

Looks Blocks

const looksBlocks = {
'looks_sayforsecs': {
message0: 'say %1 for %2 seconds',
args0: [
{ type: 'input_value', name: 'MESSAGE' },
{ type: 'input_value', name: 'SECS' }
],
category: 'looks',
colour: '#9966FF'
}
};

MistWarp Extensions

TurboWarp Blocks

Enhanced blocks for improved functionality:

const turboWarpBlocks = {
'tw_debugger': {
message0: 'breakpoint',
category: 'tw',
colour: '#FF6B6B'
},
'tw_getLastKey': {
message0: 'last key pressed',
output: 'String',
category: 'tw',
colour: '#FF6B6B'
}
};

JavaScript Blocks

Custom JavaScript integration blocks:

const javascriptBlocks = {
'js_statement': {
message0: 'run js %1',
args0: [{ type: 'input_value', name: 'CODE' }],
category: 'javascript',
colour: '#F1C40F'
}
};

Workspace Management

Workspace Initialization

const initializeWorkspace = (vm, isRtl, options) => {
const workspace = Blockly.inject('blocks-workspace', {
toolbox: generateToolbox(),
rtl: isRtl,
zoom: {
controls: true,
wheel: true,
startScale: options.zoom || 0.675
},
grid: {
spacing: 40,
length: 2,
colour: '#ddd'
},
colours: getThemeColors()
});

// Connect to VM
workspace.addChangeListener(vm.blockListener);
vm.attachBlocksWorkspace(workspace);

return workspace;
};

Dynamic Toolbox Generation

const generateToolbox = () => {
return `
<xml id="toolbox">
<category name="Motion" colour="#4C97FF">
${generateMotionBlocks()}
</category>
<category name="Looks" colour="#9966FF">
${generateLooksBlocks()}
</category>
<!-- More categories -->
</xml>
`;
};

Event Handling

Block Changes

const handleBlockChange = (event) => {
if (event.type === Blockly.Events.BLOCK_CREATE) {
onBlockCreate(event);
} else if (event.type === Blockly.Events.BLOCK_DELETE) {
onBlockDelete(event);
} else if (event.type === Blockly.Events.BLOCK_MOVE) {
onBlockMove(event);
}
};

Workspace Events

const setupWorkspaceEvents = (workspace) => {
workspace.addChangeListener((event) => {
switch (event.type) {
case Blockly.Events.UI:
handleUIEvent(event);
break;
case Blockly.Events.BLOCK_CHANGE:
handleBlockChange(event);
break;
case Blockly.Events.VAR_CREATE:
handleVariableCreate(event);
break;
}
});
};

Theming Support

Theme Integration

const applyTheme = (theme) => {
const workspace = Blockly.getMainWorkspace();

workspace.setTheme(theme);
updateBlockColors(theme);
updateWorkspaceColors(theme);
};

Custom Block Colors

const getThemeColors = () => {
const theme = getCurrentTheme();

return {
motion: theme.blocks.motion || '#4C97FF',
looks: theme.blocks.looks || '#9966FF',
sound: theme.blocks.sound || '#CF63CF',
events: theme.blocks.events || '#FFBF00',
control: theme.blocks.control || '#FFAB19',
sensing: theme.blocks.sensing || '#5CB1D6',
operators: theme.blocks.operators || '#59C059',
variables: theme.blocks.variables || '#FF8C1A',
myBlocks: theme.blocks.procedures || '#FF6680'
};
};

Performance Optimizations

Virtual Scrolling

For large workspaces with many blocks:

const VirtualizedWorkspace = () => {
const [visibleBlocks, setVisibleBlocks] = useState([]);
const workspaceRef = useRef();

const updateVisibleBlocks = useCallback(() => {
const viewport = getViewportBounds();
const visible = getAllBlocks().filter(block =>
isBlockInViewport(block, viewport)
);
setVisibleBlocks(visible);
}, []);

useEffect(() => {
const workspace = workspaceRef.current;
workspace.addChangeListener(updateVisibleBlocks);
return () => workspace.removeChangeListener(updateVisibleBlocks);
}, [updateVisibleBlocks]);

return (
<div ref={workspaceRef}>
{visibleBlocks.map(block => (
<BlockRenderer key={block.id} block={block} />
))}
</div>
);
};

Efficient Rendering

const optimizeBlockRendering = () => {
// Batch DOM updates
const batchUpdate = () => {
requestAnimationFrame(() => {
updateBlockPositions();
updateBlockConnections();
updateBlockHighlighting();
});
};

// Debounce workspace changes
const debouncedUpdate = debounce(batchUpdate, 16);

workspace.addChangeListener(debouncedUpdate);
};

Integration with VM

Block Execution

const executeBlock = (blockId) => {
const block = workspace.getBlockById(blockId);
const opcode = block.type;

// Get block inputs
const inputs = getBlockInputs(block);

// Execute via VM
return vm.runtime.executeBlock(opcode, inputs);
};

Variable Management

const createVariable = (name, type = '') => {
const variable = workspace.createVariable(name, type);

// Sync with VM
vm.createVariable(variable.getId(), name, type);

// Update blocks that use this variable
updateVariableBlocks(variable);

return variable;
};

Debugging Features

Block Highlighting

const highlightExecutingBlock = (blockId) => {
const block = workspace.getBlockById(blockId);
if (block) {
block.addSelect();
setTimeout(() => block.removeSelect(), 300);
}
};

Execution Tracing

const traceExecution = (enabled) => {
if (enabled) {
vm.runtime.on('BLOCK_EXECUTING', highlightExecutingBlock);
} else {
vm.runtime.off('BLOCK_EXECUTING', highlightExecutingBlock);
}
};

Accessibility

Keyboard Navigation

const setupKeyboardNavigation = () => {
workspace.keyboardAccessibility = new Blockly.KeyboardShortcuts(workspace);

// Custom shortcuts
workspace.keyboardAccessibility.addShortcut(
'Space',
() => executeSelectedBlock(),
'Execute selected block'
);
};

Screen Reader Support

const setupScreenReader = () => {
workspace.getAudioManager().load([
['block_created', 'Block created'],
['block_deleted', 'Block deleted'],
['block_connected', 'Blocks connected']
]);
};

Testing

Component Testing

import { render, fireEvent } from '@testing-library/react';
import { BlocksComponent } from './blocks-component';

describe('BlocksComponent', () => {
test('renders workspace correctly', () => {
const { container } = render(<BlocksComponent />);
expect(container.querySelector('#blocks-workspace')).toBeInTheDocument();
});

test('handles block creation', () => {
const onBlockCreate = jest.fn();
render(<BlocksComponent onBlockCreate={onBlockCreate} />);

// Simulate block creation
fireEvent.dragEnd(container.querySelector('.motion-block'));
expect(onBlockCreate).toHaveBeenCalled();
});
});

Integration Testing

describe('Blocks VM Integration', () => {
test('executes blocks correctly', async () => {
const vm = new VirtualMachine();
const blocksComponent = mount(<BlocksComponent vm={vm} />);

// Create and execute a simple block
const moveBlock = createMoveBlock(10);
const result = await vm.executeBlock(moveBlock);

expect(result).toBe(true);
});
});

The Blocks component is the heart of the MistWarp editing experience, providing a powerful and intuitive interface for visual programming. Its integration with the VM, theming system, and performance optimizations make it both user-friendly and efficient for creating complex projects.