Skip to content

Commit b8ddef5

Browse files
authored
Merge pull request #1036 from rgantzos/main
Fix issues with editor features
2 parents 52748b2 + 140c171 commit b8ddef5

File tree

12 files changed

+209
-94
lines changed

12 files changed

+209
-94
lines changed

api/content/redux.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Thank you to WorldLanguages, ErrorGamer2000, and apple502j
2+
3+
function injectRedux() {
4+
window.__steRedux = {};
5+
6+
class ReDucks {
7+
static compose(...composeArgs) {
8+
if (composeArgs.length === 0) return (...args) => args;
9+
return (...args) => {
10+
const composeArgsReverse = composeArgs.slice(0).reverse();
11+
let result = composeArgsReverse.shift()(...args);
12+
for (const fn of composeArgsReverse) {
13+
result = fn(result);
14+
}
15+
return result;
16+
};
17+
}
18+
19+
static applyMiddleware(...middlewares) {
20+
return (createStore) =>
21+
(...createStoreArgs) => {
22+
const store = createStore(...createStoreArgs);
23+
let { dispatch } = store;
24+
const api = {
25+
getState: store.getState,
26+
dispatch: (action) => dispatch(action),
27+
};
28+
const initialized = middlewares.map((middleware) => middleware(api));
29+
dispatch = ReDucks.compose(...initialized)(store.dispatch);
30+
return Object.assign({}, store, { dispatch });
31+
};
32+
}
33+
}
34+
35+
let newerCompose = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
36+
function compose(...args) {
37+
const steRedux = window.__steRedux;
38+
const reduxTarget = (steRedux.target = new EventTarget());
39+
steRedux.state = {};
40+
steRedux.dispatch = () => {};
41+
42+
function middleware({ getState, dispatch }) {
43+
steRedux.dispatch = dispatch;
44+
steRedux.state = getState();
45+
return (next) => (action) => {
46+
const nextReturn = next(action);
47+
const ev = new CustomEvent("statechanged", {
48+
detail: {
49+
prev: steRedux.state,
50+
next: (steRedux.state = getState()),
51+
action,
52+
},
53+
});
54+
reduxTarget.dispatchEvent(ev);
55+
return nextReturn;
56+
};
57+
}
58+
args.splice(1, 0, ReDucks.applyMiddleware(middleware));
59+
return newerCompose
60+
? newerCompose.apply(this, args)
61+
: ReDucks.compose.apply(this, args);
62+
}
63+
64+
try {
65+
Object.defineProperty(window, "__REDUX_DEVTOOLS_EXTENSION_COMPOSE__", {
66+
get: () => compose,
67+
set: (v) => {
68+
newerCompose = v;
69+
},
70+
});
71+
} catch (err) {
72+
window.__steRedux = __scratchAddonsRedux;
73+
}
74+
}
75+
76+
if (!(document.documentElement instanceof SVGElement)) {
77+
immediatelyRunFunctionInMainWorld(injectRedux);
78+
}

api/content/vm.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Thank you to mxmou, WorldLanguages, ErrorGamer2000, apple502j, TheColaber, and towerofnix
2+
3+
function immediatelyRunFunctionInMainWorld(fn) {
4+
if (typeof fn !== "function") throw "Expected function";
5+
const div = document.createElement("div");
6+
div.setAttribute("onclick", "(" + fn + ")()");
7+
document.documentElement.appendChild(div);
8+
div.click();
9+
div.remove();
10+
}
11+
12+
immediatelyRunFunctionInMainWorld(() => {
13+
const oldBind = Function.prototype.bind;
14+
window.__steTraps = new EventTarget()
15+
const onceMap = (__steTraps._onceMap = Object.create(null));
16+
17+
Function.prototype.bind = function (...args) {
18+
if (Function.prototype.bind === oldBind) {
19+
return oldBind.apply(this, args);
20+
} else if (
21+
args[0] &&
22+
Object.prototype.hasOwnProperty.call(args[0], "editingTarget") &&
23+
Object.prototype.hasOwnProperty.call(args[0], "runtime")
24+
) {
25+
onceMap.vm = args[0];
26+
Function.prototype.bind = oldBind;
27+
return oldBind.apply(this, args);
28+
} else {
29+
return oldBind.apply(this, args);
30+
}
31+
};
32+
});

api/feature.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,7 @@ class Feature {
9393
this.getInternalKey = function(element) {
9494
return Object.keys(element).find((key) => key.startsWith("__reactInternalInstance")) || null
9595
}
96-
this.redux = document.querySelector("#app")?.[
97-
Object.keys(app).find((key) => key.startsWith("__reactContainer"))
98-
].child.stateNode.store
96+
this.redux = window.__steRedux
9997
if (finalFeature.version !== 2) {
10098
console.warn(
10199
`'${finalFeature.file}' does not use Feature v2. It is recommended that you use the newest version.`

api/vm.js

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,7 @@ ScratchTools.Scratch = {
33
blockly: null,
44
};
55
try {
6-
ScratchTools.Scratch.vm =
7-
window.vm ||
8-
(() => {
9-
const app = document.querySelector("#app");
10-
return app[
11-
Object.keys(app).find((key) => key.startsWith("__reactContainer"))
12-
].child.stateNode.store.getState().scratchGui.vm;
13-
})();
6+
ScratchTools.Scratch.vm = window.vm || window.__steTraps._onceMap.vm;
147
ste.console.log("Able to load Virtual Machine.", "ste-traps");
158
} catch (err) {
169
ste.console.warn("Unable to load Virtual Machine.", "ste-traps");
@@ -28,22 +21,24 @@ try {
2821

2922
ScratchTools.Scratch.scratchSound = function () {
3023
try {
31-
return document.querySelector("div.sound-editor_editor-container_iUSW-")[
24+
let rI = document.querySelector("[class^=sound-editor_editor-container]")[
3225
Object.keys(
33-
document.querySelector("div.sound-editor_editor-container_iUSW-")
34-
).find((key) => key.startsWith("__reactInternalInstance"))
35-
].return.return.return.stateNode;
26+
document.querySelector("[class^=sound-editor_editor-container]")
27+
).find((key) => key.startsWith("__reactFiber"))
28+
];
29+
30+
while (!rI.stateNode?.audioBufferPlayer) {
31+
rI = rI.return;
32+
}
33+
return rI.stateNode;
3634
} catch (err) {
3735
return null;
3836
}
3937
};
4038

4139
ScratchTools.Scratch.scratchGui = function () {
4240
try {
43-
const app = document.querySelector("#app");
44-
return app[
45-
Object.keys(app).find((key) => key.startsWith("__reactContainer"))
46-
].child.stateNode.store.getState().scratchGui;
41+
return window.__steRedux.state.scratchGui;
4742
} catch (err) {
4843
return null;
4944
}
@@ -176,46 +171,42 @@ ScratchTools.Scratch.waitForContextMenu = function (info) {
176171
};
177172

178173
ScratchTools.Scratch.scratchPaint = function () {
179-
var app = document.querySelector(".paint-editor_mode-selector_28iiQ")||document.querySelector(".paint-editor_mode-selector_O2uhP")||document.querySelector("[class*='paint-editor_mode-selector_']");
180-
if (app !== null) {
181-
return (
182-
app[
183-
Object.keys(app).find((key) =>
184-
key.startsWith("__reactInternalInstance")
185-
)
186-
].child.stateNode.store?.getState()?.scratchPaint || null
187-
);
188-
} else {
174+
try {
175+
return __steRedux.state.scratchPaint;
176+
} catch (err) {
189177
return null;
190178
}
191179
};
192180

193-
ScratchTools.Scratch.getPaper = function () {
194-
let paintElement = document.querySelector(
195-
"[class*='paint-editor_mode-selector']"
196-
);
197-
let paintState =
198-
paintElement[
199-
Object.keys(paintElement).find((key) =>
200-
key.startsWith("__reactInternalInstance")
201-
)
202-
].child;
181+
window.__paperCache = null
182+
183+
async function getPaper() {
184+
const modeSelector = document.querySelector("[class*='paint-editor_mode-selector']");
185+
const internalState = modeSelector[Object.keys(modeSelector).find((el) => el.startsWith("__reactFiber"))].child;
186+
let toolState = internalState;
203187
let tool;
204-
while (paintState) {
205-
let paintIn = paintState.child?.stateNode;
206-
if (paintIn?.tool) {
207-
tool = paintIn.tool;
188+
while (toolState) {
189+
const toolInstance = toolState.child.child.stateNode;
190+
if (toolInstance.tool) {
191+
tool = toolInstance.tool;
208192
break;
209193
}
210-
if (paintIn?.blob && paintIn?.blob.tool) {
211-
tool = paintIn.blob.tool;
194+
if (toolInstance.blob && toolInstance.blob.tool) {
195+
tool = toolInstance.blob.tool;
212196
break;
213197
}
214-
paintState = paintState.sibling;
198+
toolState = toolState.sibling;
215199
}
216200
if (tool) {
217-
return tool._scope;
201+
const paperScope = tool._scope;
202+
window.__paperCache = paperScope
203+
return paperScope;
218204
}
205+
return null
206+
}
207+
208+
ScratchTools.Scratch.getPaper = async function () {
209+
return await getPaper()
219210
};
220211

221212
async function alertForUpdates() {

features/dark-paint-editor/script.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ export default async function ({ feature, console, scratchClass }) {
4444
}
4545
);
4646

47-
function updateTheme(isDark) {
48-
let paper = feature.traps.getPaper();
47+
async function updateTheme(isDark) {
48+
let paper = await feature.traps.getPaper();
4949

5050
let backgroundLayer = paper.project.layers.find(
5151
(el) => el.data?.["isBackgroundGuideLayer"]

features/more-paint-functions/script.js

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
export default async function ({ feature, console, scratchClass }) {
2-
function unite() {
3-
let paper = feature.traps.getPaper();
2+
async function unite() {
3+
let paper = await feature.traps.getPaper();
44
let items = paper.project.selectedItems;
55

66
if (items.length !== 2) return;
@@ -18,8 +18,8 @@ export default async function ({ feature, console, scratchClass }) {
1818
paper.tool.onUpdateImage();
1919
}
2020

21-
function subtract() {
22-
let paper = feature.traps.getPaper();
21+
async function subtract() {
22+
let paper = await feature.traps.getPaper();
2323
let items = paper.project.selectedItems;
2424

2525
if (items.length !== 2) return;
@@ -37,8 +37,8 @@ export default async function ({ feature, console, scratchClass }) {
3737
paper.tool.onUpdateImage();
3838
}
3939

40-
function exclude() {
41-
let paper = feature.traps.getPaper();
40+
async function exclude() {
41+
let paper = await feature.traps.getPaper();
4242
let items = paper.project.selectedItems;
4343

4444
if (items.length !== 2) return;
@@ -56,8 +56,8 @@ export default async function ({ feature, console, scratchClass }) {
5656
paper.tool.onUpdateImage();
5757
}
5858

59-
function intersect() {
60-
let paper = feature.traps.getPaper();
59+
async function intersect() {
60+
let paper = await feature.traps.getPaper();
6161
let items = paper.project.selectedItems;
6262

6363
if (items.length !== 2) return;
@@ -112,23 +112,25 @@ export default async function ({ feature, console, scratchClass }) {
112112
}
113113
);
114114

115-
feature.redux.subscribe(function () {
116-
if (document.querySelector(".ste-more-functions")) {
117-
let span = document.querySelector(".ste-more-functions");
118-
if (
119-
feature.traps.paint().format === "BITMAP" ||
120-
feature.traps.paint().selectedItems?.length < 2
121-
) {
122-
document.querySelectorAll(".ste-more-functions").forEach(function (el) {
123-
el.classList.add("button_mod-disabled_1rf31");
124-
});
125-
} else {
126-
document.querySelectorAll(".ste-more-functions").forEach(function (el) {
127-
el.classList.remove("button_mod-disabled_1rf31");
128-
});
115+
feature.redux.target.addEventListener("statechanged", function(e) {
116+
if (e.detail.action.type.startsWith("scratch-paint/")) {
117+
if (document.querySelector(".ste-more-functions")) {
118+
let span = document.querySelector(".ste-more-functions");
119+
if (
120+
feature.traps.paint().format === "BITMAP" ||
121+
feature.traps.paint().selectedItems?.length < 2
122+
) {
123+
document.querySelectorAll(".ste-more-functions").forEach(function (el) {
124+
el.classList.add("button_mod-disabled_1rf31");
125+
});
126+
} else {
127+
document.querySelectorAll(".ste-more-functions").forEach(function (el) {
128+
el.classList.remove("button_mod-disabled_1rf31");
129+
});
130+
}
129131
}
130132
}
131-
});
133+
})
132134

133135
function makeButton({ name, icon, callback }) {
134136
let span = document.createElement("span");

features/opacity-slider/script.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,16 @@ export default function ({ feature, console, scratchClass }) {
5858

5959
let lastColor;
6060

61-
feature.redux.subscribe(function () {
61+
feature.redux.target.addEventListener("statechanged", function(e) {
62+
if (e.detail.action.type.startsWith("scratch-paint/")) {
6263
let slider = document.querySelector(".ste-opacity-background");
6364
let newColor =
6465
feature.traps.paint?.()?.selectedItems[0]?.fillColor?._canvasStyle;
6566
if (!slider) return;
6667
if (newColor === lastColor) return;
6768
lastColor = newColor;
6869
slider.style.background = `linear-gradient(270deg, ${lastColor} 0%, rgba(0, 0, 0, 0) 100%)`;
70+
}
6971
});
7072

7173
function handleSlider(handle, value) {

features/outline-shape-options/script.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ export default async function ({ feature, console }) {
44
Join: ["miter", "round", "bevel", "arcs", "miter-clip"],
55
};
66

7-
function createSection(type) {
8-
const selectedItems = feature.traps.getPaper().project.selectedItems;
7+
async function createSection(type) {
8+
const selectedItems = (await feature.traps.getPaper()).project.selectedItems;
99
const result = document.createElement("div");
1010
let strokeValue = undefined;
1111

@@ -64,13 +64,13 @@ export default async function ({ feature, console }) {
6464

6565
ScratchTools.waitForElements(
6666
"div[class*='color-picker_swatch-row_']",
67-
function (element) {
67+
async function (element) {
6868
if (feature.traps.paint().modals.fillColor) return;
69-
if (feature.traps.getPaper().project.selectedItems.length < 1) return;
69+
if ((await feature.traps.getPaper())?.project.selectedItems.length < 1) return;
7070
const dividerLine = document.createElement("div");
7171
dividerLine.classList.add("color-picker_divider_3a3qR");
72-
const sectionCap = createSection("Cap");
73-
const sectionJoin = createSection("Join");
72+
const sectionCap = await createSection("Cap");
73+
const sectionJoin = await createSection("Join");
7474

7575
element.insertAdjacentElement("afterend", sectionJoin);
7676
element.insertAdjacentElement("afterend", sectionCap);

0 commit comments

Comments
 (0)