Hey everyone,
I’m running into a issue with the backpack feature in Blockly, and I’d really appreciate your help. I’m working on an issue originally created by @Susan_Lane on GitHub #3388 Issue Link, and I’ve been digging into it, but I’m stuck. Here’s the full breakdown of the problem, along with the code and logs, so you can see exactly what’s happening. I’m keeping the code and logs unchanged as they are, and I just want to figure out what’s going wrong.
The Issue
When I rename a procedure block in the workspace from its default name (procedure
) to a custom name like myBlock
and then add it to the backpack, everything seems to work fine at first. The block gets saved to the backpack with the custom name intact. But here’s the problem: when I open the backpack, the block shows up in the flyout with the default name procedure
instead of myBlock
. I’ve added logs to the two main functions involved—addToBackpack
and openBackpack
—to investigate, and I’ll share those below. I’m super confused because the logs show the custom name being stored correctly, yet it’s not displaying right in the flyout. Where’s the issue hiding?
Code and Logs
1. addToBackpack
Function
This function takes the renamed block and saves it to the backpack. Here’s the code:
addToBackpack(block, store) {
// Copy is made of the expanded block.
const isCollapsed = block.isCollapsed();
block.setCollapsed(false);
const xmlBlock = Blockly.Xml.blockToDom(block);
console.dir('Adding block to backpack: ' + xmlBlock);
Blockly.Xml.deleteNext(xmlBlock);
// Encode start position in XML.
const xy = block.getRelativeToSurfaceXY();
xmlBlock.setAttribute('x', this.workspace_.RTL ? -xy.x : xy.x);
xmlBlock.setAttribute('y', xy.y);
block.setCollapsed(isCollapsed);
// Add the block to the backpack
this.getContents()
.then((contents) => {
console.log('Contents of add block: ' + contents);
if (!contents) {
contents = [];
}
contents.push("<xml>" + Blockly.Xml.domToText(xmlBlock) + "</xml>");
console.log("Blockly.Xml.domToText(xmlBlock)::: " + Blockly.Xml.domToText(xmlBlock))
console.log('Contents after add block: ' + contents);
this.setContents(contents, store);
this.grow();
Blockly.common.getMainWorkspace().getAudioManager().play('backpack');
if (this.flyout_.isVisible()) {
this.isAdded = true;
this.openBackpack();
this.isAdded = false;
}
});
}
And here are the logs from this function:
Adding block to backpack: [object Element]
aiblockly-0.nocache.js:66932 getContents called
aiblockly-0.nocache.js:66953 Returning existing contents: []0: "<xml><block xmlns=\"https://developers.google.com/blockly/xml\" type=\"procedures_defnoreturn\" id=\"m,+sL`%rNyA:M0Y4B8pD\" x=\"-120.44113043030438\" y=\"-718.8873915669743\"><field name=\"NAME\">myBlock</field></block></xml>"length: 1[[Prototype]]: Array(0)
aiblockly-0.nocache.js:66862 Contents of add block:
aiblockly-0.nocache.js:66867 Blockly.Xml.domToText(xmlBlock)::: <block xmlns="https://developers.google.com/blockly/xml" type="procedures_defnoreturn" id="m,+sL`%rNyA:M0Y4B8pD" x="-120.44113043030438" y="-718.8873915669743"><field name="NAME">myBlock</field></block>
aiblockly-0.nocache.js:66868 Contents after add block: <xml><block xmlns="https://developers.google.com/blockly/xml" type="procedures_defnoreturn" id="m,+sL`%rNyA:M0Y4B8pD" x="-120.44113043030438" y="-718.8873915669743"><field name="NAME">myBlock</field></block></xml>
What I see here:
the addToBackpack function works correctly:
-
It captures the block’s full XML, including the custom name "myBlock."
-
It stores the XML in the backpack’s contents properly, as confirmed by the log.
-
It handles collapse state, position, and UI updates as intended.
-
The log shows no errors—no missing data, no corruption of the XML, and the custom name is intact.
2. openBackpack
Function
This function retrieves the blocks from the backpack and shows them in the flyout. Here’s the code:
AI.Blockly.Backpack.prototype.openBackpack = function(e) {
if (e) {
e.stopPropagation();
if (e.button === 2) {
this.flyout_.hide();
this.openBackpackMenu(e);
return;
}
}
if (!this.isAdded && this.flyout_.isVisible()) {
this.flyout_.hide();
} else {
this.getContents().then((contents) => {
console.warn('Backpack contents: ' + contents);
const len = contents.length;
console.warn('Backpack contents length: ' + len);
const newBackpack = [];
for (let i = 0; i < len; i++) {
newBackpack[i] = Blockly.Versioning.upgradeComponentMethods(Blockly.utils.xml.textToDom(contents[i]).firstChild);
console.dir('newBackpack[i] outerHTML: ' + newBackpack[i].outerHTML);
}
Blockly.hideChaff();
console.log('newBackpack:', newBackpack); // Logs the full array properly
console.dir(newBackpack); // Logs as an inspectable tree in dev tools
console.log('newBackpack (outerHTML):', newBackpack.map(el => el.outerHTML)); // Logs readable HTML structure
this.flyout_.show(newBackpack);
});
}
};
And here are the logs from this function:
aiblockly-0.nocache.js:67155 Backpack contents: <xml><block xmlns="https://developers.google.com/blockly/xml" type="procedures_defnoreturn" id="m,+sL`%rNyA:M0Y4B8pD" x="-168.8942963416888" y="-715.102479636611"><field name="NAME">myBlock</field></block></xml>
(anonymous) @ aiblockly-0.nocache.js:67155
Promise.then
AI.Blockly.Backpack.openBackpack @ aiblockly-0.nocache.js:67154
e @ aiblockly-0.nocache.js:3948
aiblockly-0.nocache.js:67157 Backpack contents length: 1
(anonymous) @ aiblockly-0.nocache.js:67157
Promise.then
AI.Blockly.Backpack.openBackpack @ aiblockly-0.nocache.js:67154
e @ aiblockly-0.nocache.js:3948
aiblockly-0.nocache.js:67161 newBackpack[i] outerHTML: <block xmlns="https://developers.google.com/blockly/xml" type="procedures_defnoreturn" id="m,+sL`%rNyA:M0Y4B8pD" x="-168.8942963416888" y="-715.102479636611"><field name="NAME">myBlock</field></block>
aiblockly-0.nocache.js:67165 newBackpack: [block#m,+sL`%rNyA:M0Y4B8pD]
aiblockly-0.nocache.js:67166 Array(1)0: block#m,+sL`%rNyA:M0Y4B8pDlength: 1[[Prototype]]: Array(0)
aiblockly-0.nocache.js:67167 newBackpack (outerHTML): ['<block xmlns="https://developers.google.com/blockl…36611"><field name="NAME">myBlock</field></block>']
What I see here:
- The backpack contents are retrieved, and the log shows
<field name="NAME">myBlock</field>
—so the custom name is still there. - The XML is converted back to a DOM element, and
newBackpack[i].outerHTML
confirms thatmyBlock
is present in the block’s structure. - The
newBackpack
array is passed tothis.flyout_.show(newBackpack)
, and up to this point, everything looks correct—the custom name is stillmyBlock
. - But when the flyout actually displays the block, it shows up as
procedure
instead. What’s going on?!
My Thoughts and Where I’m Stuck
From the logs, it’s clear that:
addToBackpack
stores the block with the custom namemyBlock
correctly.openBackpack
retrieves the block, and even after theBlockly.Versioning.upgradeComponentMethods
call, the outerHTML still showsmyBlock
.- The problem only happens when the flyout renders the block—somehow, the custom name gets replaced with
procedure
.
I’m starting to think the issue might be in this.flyout_.show(newBackpack)
. Does the flyout have some special logic for procedure blocks that ignores the custom name in the XML and resets it to the default procedure
? Or could Blockly.Versioning.upgradeComponentMethods
be doing something sneaky that only affects the display, even though the outerHTML looks fine?
I’m bit frustrated because the logs show everything working up until the flyout displays the block.
I’d love to hear your thoughts or any ideas on how to debug this further—maybe where to add more logs or what to check next.
Thanks so much for any help!