// ==UserScript==
// @name scratch - show unused blocks
// @version 0.1
// @description adds a unused blocks tab to the debug panel of scratch addons that shows all custom blocks that exist but are not called and all that call to a nonexistant custom block
// @license GPLv3
// @run-at document-start
// @author You
// @match *://scratch.mit.edu/*
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAAAXNSR0IB2cksfwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAHJQTFRFAAAAEIijAo2yAI60BYyuF4WaFIifAY6zBI2wB4usGIaZEYigIoiZCIyrE4igG4iYD4mjEomhFoedCoqpDIqnDomlBYyvE4efEYmiDYqlA42xBoytD4mkCYqqGYSUFYidC4qoC4upAo6yCoupDYqmCYur4zowOQAAACZ0Uk5TAO////9vr////1+/D/+/L+/Pf/////+f3///////H4////////+5G91rAAACgUlEQVR4nM2Y22KjIBCGidg1264liZqDadK03X3/V2wNKHMC7MpF/xthHD5mgERAqZhWhfYqH6K+Qf2qNNf625hCoFj9/gblMUi5q5jLkXLCKudgyiRm0FMK82cWJp1fLbV5VmvJbCIc0GCYaFqqlDJgADdBjncqAXYobm1xh72aFMflbysteFfdy2Yi1XGOm5HGBzQ1dq7TzEoxjeNTjQZb7VA3e1c7+ImgasAgQ9+xusNVNZIo5xmOMgihIS2PbCQIiHEUdTvhxCcS/kPomfFI2zHy2PkWmA6aNatIJpKFJyekyy02xh5Y3DI9T4aOT6VhIUrsNTFp1pf79Z4SIIVDegl6IJO6cHiL/GimIZDhgTu/BlYWCQzHMl0zBWT/T3KAhtxOuUB9FtBrpsz0RV4xsjHmW+UCaffcSy/5viMGer0/6HdFNMZBq/vjJL38H9Dqx4Fuy0Em12DbZy+9pGtiDijbglwAehyj11n0tRD3WUBm+lwulE/8h4BuA+iWAQQnteg2Xm63WQLTpnMnpjdge0Mgu/GRPsV4xdjQ94Lfi624fabhDkfUqIKNrM64Q837v8yL0prasepCgrtvw1sJpoqanGEX7b5mQboNW8eawXaWXTMfMGxub472hzWzHSn6Sg2G9+6TAyRruE71s+zAzjWaknoyJCQzwxrghH2k5FDT4eqWunuNxyN9QCGcxVod5oADbYnIUkDTGZEf1xDJnSFteQ3KdsT8zYDMQXcHxsevcLH1TrsABzkNPyA/L7b0jg704viMMlpQI96WsHknCt/3YH0kOEo9zcGkwrFK39ck72rmoehmKqo2RKlilzSy/nJKEV45CT38myJp456fezktHjN5aeMAAAAASUVORK5CYII=
// @grant none
// @namespace https://gf.qytechs.cn/users/1184528
// ==/UserScript==
;(async () => {
await a(".sa-debugger-tabs>li").waitforelem()
var unusedblocks = a(".sa-debugger-tabs").createelem("li", {
class: "scratchCategoryId-myBlocks",
innerHTML: `<div class="scratchCategoryItemBubble" style="background-color: rgb(94, 30, 63); border-color: rgb(255, 0, 132);"></div>unused blocks`,
onclick(e) {
e.stopImmediatePropagation()
a(".sa-debugger-tab-selected")
.qs()
.val.classList.remove("sa-debugger-tab-selected")
this.classList.add("sa-debugger-tab-selected")
var content = a(".sa-debugger-tab-content").qs().val
content.innerHTML = ""
var arrs = {}
vm.runtime.targets.forEach((target) =>
Object.entries(target.blocks._blocks)
.filter(
(target) =>
target[1].opcode == "procedures_call" ||
target[1].opcode == "procedures_prototype"
)
.forEach((e) => {
if (!arrs[target.sprite.name])
arrs[target.sprite.name] = {
procedures_prototype: [],
procedures_call: [],
}
arrs[target.sprite.name][e[1].opcode].push(e[1]?.mutation?.proccode)
})
)
var parents = {}
Object.entries(arrs).forEach(
([name, { procedures_call, procedures_prototype }]) => {
procedures_call.forEach((call) => {
if (!procedures_prototype.includes(call)) {
if (
[
"breakpoint",
"warn %s",
"log %s",
"error %s",
"breakpoint",
].includes(call)
)
return
var parent =
parents[name] ??
a(content).createelem("div", {
id: "ubpar" + name,
width: "calc(100% - 8px)",
border:
vm.editingTarget.sprite.name == name
? "4px solid #090"
: "4px solid #900",
backgroundColor:
vm.editingTarget.sprite.name == name ? "#070" : "#700",
innerHTML: `${name}`,
})
parents[name] ??= parent
a(parent).createelem("div", {
width: "calc(100% - 48px)",
position: "relative",
left: "40px",
border: "4px solid #990",
backgroundColor: "#770",
innerHTML: `call to non existent function "${call}"`,
onclick() {
if (vm.editingTarget.sprite.name == name)
goto("define " + call)
},
})
}
})
procedures_prototype.forEach((prototype) => {
if (!procedures_call.includes(prototype)) {
var parent =
parents[name] ??
a(content).createelem("div", {
id: "ubpar" + name,
width: "calc(100% - 8px)",
border:
vm.editingTarget.sprite.name == name
? "4px solid #090"
: "4px solid #900",
backgroundColor:
vm.editingTarget.sprite.name == name ? "#070" : "#700",
innerHTML: `${name}`,
})
parents[name] ??= parent
a(parent).createelem("div", {
width: "calc(100% - 48px)",
position: "relative",
left: "40px",
border: "4px solid #990",
backgroundColor: "#770",
innerHTML: `unused function "${prototype}"`,
onclick() {
if (vm.editingTarget.sprite.name == name)
goto("define " + prototype)
},
})
}
})
}
)
},
}).val
a(".sa-debugger-tabs>li")
.qsa()
.map((e) =>
a(e).listen("click", function (e) {
log(e.target)
if (e.target !== unusedblocks)
unusedblocks.classList.remove("sa-debugger-tab-selected")
})
)
function goto(text) {
var find = a("#sa-find-input").qs().val
find.value = text
// find.blur()
find.focus()
a('.sa-find-dropdown>li[style="display: block;"]')
.qs()
.val.dispatchEvent(
new MouseEvent("mousedown", {
bubbles: true,
cancelable: true,
view: window,
})
)
}
})()