Advanced Block Customization
This page covers advanced techniques for customizing block appearance and behavior beyond standard extension development.
Visual Block Replacement
TurboWarp allows extensions to completely replace block visuals with custom content, including images, videos, and interactive elements. This technique involves manipulating the DOM after blocks are rendered.
Basic Image Replacement
Here's how the Image Blocks extension replaces block content with custom SVG images:
function injectCustomVisuals(categoryName) {
document
.querySelector('g.blocklyWorkspace')
.querySelectorAll(`g[data-category="${categoryName}"]`)
.forEach((g) => {
// Get the block data to identify which block this is
let block = vm.runtime.getEditingTarget().blocks.getBlock(g.dataset.id);
if (block && !g.querySelector('svg#customIcon')) {
if (block.opcode === 'myExtension_imageBlock') {
// Replace the block's innerHTML with custom SVG
g.innerHTML = `<svg id="customIcon" width="92" height="92" viewBox="0,0,92,92">
<image href="https://example.com/cat.png" height="92" width="92" />
</svg><!--rotationCenter:46:46-->`;
}
}
});
}
Triggering Visual Updates
The visual replacement needs to be triggered whenever blocks are updated:
// Hook into VM events to update visuals
vm.runtime.on('PROJECT_CHANGED', () => injectCustomVisuals('My Extension'));
vm.runtime.on('BLOCK_DRAG_UPDATE', () => injectCustomVisuals('My Extension'));
vm.runtime.on('BLOCK_DRAG_END', () => injectCustomVisuals('My Extension'));
Advanced Content Types
Video Content
You can embed video content using foreignObject:
g.innerHTML = `<svg width="300" height="200" viewBox="0,0,300,200">
<foreignObject width="300" height="200">
<video xmlns="https://example.com/cat.png" width="300" height="200" autoplay loop muted>
<source src="https://example.com/cat.png" type="video/mp4" />
</video>
</foreignObject>
</svg><!--rotationCenter:150:100-->`;
Interactive Content
Even interactive applications can be embedded:
g.innerHTML = `<svg width="640" height="400" viewBox="0,0,640,400">
<foreignObject width="640" height="400">
<iframe width="640" height="400" src="data:text/html;base64,${encodedHTML}"></iframe>
</foreignObject>
</svg><!--rotationCenter:320:200-->`;