From eb2af9979fb2a620d0f28145bb1365f2f770c710 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:07:36 +0100 Subject: [PATCH 01/17] add pre-locale to avoid warning --- src/hooks/useProject.js | 8 +++++++- src/hooks/useProject.test.js | 7 ++++++- src/utils/defaultProjects.js | 6 +++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/hooks/useProject.js b/src/hooks/useProject.js index b5bd4483f..98c110621 100644 --- a/src/hooks/useProject.js +++ b/src/hooks/useProject.js @@ -105,7 +105,13 @@ export const useProject = ({ return; } - const data = defaultPythonProject; + const data = { + ...defaultPythonProject, + name: i18n.t("project.untitled", { + lng: effectiveLocale, + defaultValue: defaultPythonProject.name, + }), + }; dispatch(setProject(data)); } }, [ diff --git a/src/hooks/useProject.test.js b/src/hooks/useProject.test.js index 7b52cb615..7198e4aaf 100644 --- a/src/hooks/useProject.test.js +++ b/src/hooks/useProject.test.js @@ -57,7 +57,12 @@ describe("When not embedded", () => { test("If no identifier uses default python project", () => { renderHook(() => useProject({}), { wrapper }); - expect(setProject).toHaveBeenCalledWith(defaultPythonProject); + expect(setProject).toHaveBeenCalledWith( + expect.objectContaining({ + ...defaultPythonProject, + name: "project.untitled", + }), + ); }); test("sets project to initialProject if provided", () => { diff --git a/src/utils/defaultProjects.js b/src/utils/defaultProjects.js index abacf349f..dcc9d9e01 100644 --- a/src/utils/defaultProjects.js +++ b/src/utils/defaultProjects.js @@ -1,8 +1,8 @@ -import i18n from "./i18n"; +const UNTITLED_PROJECT_NAME = "Untitled project"; export const defaultPythonProject = { project_type: "python", - name: i18n.t("project.untitled"), + name: UNTITLED_PROJECT_NAME, locale: null, components: [{ extension: "py", name: "main", content: "", default: true }], image_list: [], @@ -10,7 +10,7 @@ export const defaultPythonProject = { export const defaultHtmlProject = { project_type: "html", - name: i18n.t("project.untitled"), + name: UNTITLED_PROJECT_NAME, components: [ { extension: "html", From e3ecbaf375b6243278212f4f7bbf4d6240095f5d Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:34:27 +0100 Subject: [PATCH 02/17] missing key --- src/components/Menus/Sidebar/FilePanel/FilePanel.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/Menus/Sidebar/FilePanel/FilePanel.jsx b/src/components/Menus/Sidebar/FilePanel/FilePanel.jsx index 39946fbcd..f56d24753 100644 --- a/src/components/Menus/Sidebar/FilePanel/FilePanel.jsx +++ b/src/components/Menus/Sidebar/FilePanel/FilePanel.jsx @@ -52,6 +52,7 @@ const FilePanel = ({ isMobile }) => { ? [] : [ } From e37efbe85ec5d58ad8f0d2b172112585e3eb2be9 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:38:19 +0100 Subject: [PATCH 03/17] remove invalid prop - The initial tab comes from: SkulptRunner.jsx:480 - That state is kept in sync with showVisualOutput in SkulptRunner.jsx:484 - No need for that prop --- .../Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx index 5c3f9ff35..b444a341e 100644 --- a/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx +++ b/src/components/Editor/Runners/PythonRunner/SkulptRunner/SkulptRunner.jsx @@ -558,7 +558,6 @@ const SkulptRunner = ({ active, outputPanels = ["text", "visual"] }) => { ) : ( From c01d480ced1efb30c0c796f13f8f43a89879e5a1 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:40:47 +0100 Subject: [PATCH 04/17] unnecesary log --- .../Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx index 0d39afe80..aae5eb45b 100644 --- a/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx +++ b/src/components/Editor/Runners/PythonRunner/PyodideRunner/PyodideRunner.jsx @@ -366,8 +366,7 @@ const PyodideRunner = ({ active, outputPanels = ["text", "visual"] }) => { }; if (!pyodideWorker && active) { - console.warn("PyodideWorker is not initialized"); - return; + return null; } return ( From 2bb82328c382fffaf900fe6cafef00e77f83ac02 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 14:43:12 +0100 Subject: [PATCH 05/17] avoid web-component loading icon --- src/web-component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/web-component.html b/src/web-component.html index f65c1414b..4ccd277d4 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -50,6 +50,7 @@ + Editor Web component From 6614735d1d38b71d7de373e3871c6d2c57e84252 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 15:26:08 +0100 Subject: [PATCH 06/17] toast warning removal --- src/containers/WebComponentLoader.jsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/containers/WebComponentLoader.jsx b/src/containers/WebComponentLoader.jsx index f468a97bd..5f6e2d30a 100644 --- a/src/containers/WebComponentLoader.jsx +++ b/src/containers/WebComponentLoader.jsx @@ -32,6 +32,15 @@ import { projectOwnerLoadedEvent, } from "../events/WebComponentCustomEvents"; +const TOAST_CONTAINER_DEFAULTS = { + ...(ToastContainer.defaultProps || {}), +}; + +// react-toastify v8 uses defaultProps on a function component, which React +// warns about in development. We pass the same defaults explicitly instead. +// We should upgrade to version 10 in a different commit, this removes the warning +ToastContainer.defaultProps = undefined; + const WebComponentLoader = (props) => { const { assetsIdentifier, @@ -222,6 +231,7 @@ const WebComponentLoader = (props) => { {internalStyles.toString()}
Date: Mon, 16 Mar 2026 15:39:41 +0100 Subject: [PATCH 07/17] show warning only once - avoid noise when this "DEPRECATED: icons as React elements will not be supported in future releases" is emitted several times --- src/web-component.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/web-component.js b/src/web-component.js index 2ac855093..054b28e70 100644 --- a/src/web-component.js +++ b/src/web-component.js @@ -11,6 +11,21 @@ import { stopCodeRun, stopDraw, triggerCodeRun } from "./redux/EditorSlice"; import { BrowserRouter } from "react-router-dom"; import { resetStore } from "./redux/RootSlice"; +const originalWarn = console.warn.bind(console); +let hasShownDesignSystemIconWarning = false; +const warningText = "DEPRECATED: icons as React elements will not be supported in future releases"; +// This should be addressed by applying the fix, in @raspberrypifoundation/design-system-react +// This shows the warning once instead of N times +console.warn = (...args) => { + if (args[0] === warningText) { + if (hasShownDesignSystemIconWarning) { + return; + } + hasShownDesignSystemIconWarning = true; + } + originalWarn(...args); +}; + Sentry.init({ dsn: process.env.REACT_APP_SENTRY_DSN, integrations: [new BrowserTracing()], From 2ba7350196418117ff0eb846411d091bded0b210 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:06:03 +0100 Subject: [PATCH 08/17] reduce warnings by early destruct --- .../ScratchEditor/ScratchIntegrationHOC.jsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx b/src/components/ScratchEditor/ScratchIntegrationHOC.jsx index 87bea0424..927560748 100644 --- a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx +++ b/src/components/ScratchEditor/ScratchIntegrationHOC.jsx @@ -43,6 +43,10 @@ const ScratchIntegrationHOC = function (WrappedComponent) { return; } + if (event.data?.type === "webpackOk") { + return; + } + switch (event.data.type) { case "scratch-gui-download": this.handleDownload(event); @@ -80,7 +84,16 @@ const ScratchIntegrationHOC = function (WrappedComponent) { this.props.onClickSave(); } render() { - const { ...componentProps } = this.props; + const { + loadProject, + localesOnly, + onClickRemix, + onClickSave, + saveProjectSb3, + setStageSize, + ...componentProps + } = this.props; + return ; } } From f7b96a6f8a8f97e9a5947e073af3508463c22b92 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:25:43 +0100 Subject: [PATCH 09/17] capture and de-dupe scratch warnings to avoid console flooding --- src/scratch.jsx | 3 + src/utils/dedupeScratchWarnings.js | 96 ++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/utils/dedupeScratchWarnings.js diff --git a/src/scratch.jsx b/src/scratch.jsx index ca1d4a150..444a266df 100644 --- a/src/scratch.jsx +++ b/src/scratch.jsx @@ -5,9 +5,12 @@ import { compose } from "redux"; import GUI, { AppStateHOC } from "@scratch/scratch-gui"; import ScratchIntegrationHOC from "./components/ScratchEditor/ScratchIntegrationHOC.jsx"; +import dedupeScratchWarnings from "./utils/dedupeScratchWarnings.js"; import ScratchStyles from "./assets/stylesheets/Scratch.scss"; +dedupeScratchWarnings(); + const appTarget = document.getElementById("app"); document.getElementById("scratch-loading")?.remove(); GUI.setAppElement(appTarget); diff --git a/src/utils/dedupeScratchWarnings.js b/src/utils/dedupeScratchWarnings.js new file mode 100644 index 000000000..49e4802d2 --- /dev/null +++ b/src/utils/dedupeScratchWarnings.js @@ -0,0 +1,96 @@ +const scratchConsoleBrokerKey = "__scratchConsoleBrokerInstalled"; + +const scratchWarningMatchers = [ + { + method: "error", + needle: "Support for defaultProps will be removed", + id: "react-default-props", + summary: + "React 18 compatibility warnings about function-component defaultProps", + }, + { + method: "error", + needle: "React does not recognize the `%s` prop on a DOM element", + id: "react-dom-props", + summary: "React warnings about scratch-editor props leaking onto DOM nodes", + }, + { + method: "error", + needle: "Unknown event handler property `%s`", + id: "react-event-props", + summary: "React warnings about unsupported event handler props", + }, + { + method: "warn", + needle: "componentWillMount has been renamed", + id: "react-component-will-mount", + summary: "React warnings about legacy componentWillMount usage", + }, + { + method: "warn", + needle: "componentWillReceiveProps has been renamed", + id: "react-component-will-receive-props", + summary: "React warnings about legacy componentWillReceiveProps usage", + }, + { + method: "error", + needle: "findDOMNode is deprecated", + id: "react-find-dom-node", + summary: "React warnings about deprecated findDOMNode usage", + }, +]; + +const getScratchConsoleCategory = (method, args) => { + const message = typeof args[0] === "string" ? args[0] : ""; + const matcher = scratchWarningMatchers.find( + (candidate) => + candidate.method === method && message.includes(candidate.needle), + ); + + return matcher || null; +}; + +const dedupeScratchWarnings = () => { + if (process.env.NODE_ENV === "production" || typeof window !== "object") { + return; + } + + if (window[scratchConsoleBrokerKey]) { + return; + } + + window[scratchConsoleBrokerKey] = true; + + const seenCategories = new Set(); + const originalError = console.error.bind(console); + const originalWarn = console.warn.bind(console); + + const emitCategorySummary = (category) => { + if (seenCategories.has(category.id)) { + return; + } + + seenCategories.add(category.id); + originalWarn( + `[scratch-editor] emitted ${category.summary}. Further duplicates suppressed.`, + ); + }; + + const wrapConsoleMethod = (method, originalMethod) => { + return (...args) => { + const category = getScratchConsoleCategory(method, args); + + if (category) { + emitCategorySummary(category); + return; + } + + originalMethod(...args); + }; + }; + + console.error = wrapConsoleMethod("error", originalError); + console.warn = wrapConsoleMethod("warn", originalWarn); +}; + +export default dedupeScratchWarnings; From bf24876c37c70bd2471bb53362f08766f8f4285d Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:30:37 +0100 Subject: [PATCH 10/17] create a utility following pattern for design system warnings --- src/utils/dedupeDesignSystemWarnings.js | 32 +++++++++++++++++++++++++ src/web-component.js | 16 ++----------- 2 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 src/utils/dedupeDesignSystemWarnings.js diff --git a/src/utils/dedupeDesignSystemWarnings.js b/src/utils/dedupeDesignSystemWarnings.js new file mode 100644 index 000000000..e275eef45 --- /dev/null +++ b/src/utils/dedupeDesignSystemWarnings.js @@ -0,0 +1,32 @@ +const designSystemWarningsKey = "__designSystemWarningsDeduped"; +const designSystemIconWarning = + "DEPRECATED: icons as React elements will not be supported in future releases"; + +const dedupeDesignSystemWarnings = () => { + if (process.env.NODE_ENV === "production" || typeof window !== "object") { + return; + } + + if (window[designSystemWarningsKey]) { + return; + } + + window[designSystemWarningsKey] = true; + + const originalWarn = console.warn.bind(console); + let hasShownDesignSystemIconWarning = false; + + console.warn = (...args) => { + if (args[0] === designSystemIconWarning) { + if (hasShownDesignSystemIconWarning) { + return; + } + + hasShownDesignSystemIconWarning = true; + } + + originalWarn(...args); + }; +}; + +export default dedupeDesignSystemWarnings; diff --git a/src/web-component.js b/src/web-component.js index 054b28e70..244cae1f9 100644 --- a/src/web-component.js +++ b/src/web-component.js @@ -10,21 +10,9 @@ import camelCase from "camelcase"; import { stopCodeRun, stopDraw, triggerCodeRun } from "./redux/EditorSlice"; import { BrowserRouter } from "react-router-dom"; import { resetStore } from "./redux/RootSlice"; +import dedupeDesignSystemWarnings from "./utils/dedupeDesignSystemWarnings"; -const originalWarn = console.warn.bind(console); -let hasShownDesignSystemIconWarning = false; -const warningText = "DEPRECATED: icons as React elements will not be supported in future releases"; -// This should be addressed by applying the fix, in @raspberrypifoundation/design-system-react -// This shows the warning once instead of N times -console.warn = (...args) => { - if (args[0] === warningText) { - if (hasShownDesignSystemIconWarning) { - return; - } - hasShownDesignSystemIconWarning = true; - } - originalWarn(...args); -}; +dedupeDesignSystemWarnings(); Sentry.init({ dsn: process.env.REACT_APP_SENTRY_DSN, From 54483aa66f4a8f3bf5cdae34a583afd3c554effc Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:32:20 +0100 Subject: [PATCH 11/17] lint after changes --- .../Editor/Runners/PythonRunner/OutputViewToggle.jsx | 4 ++-- src/components/Menus/Sidebar/Sidebar.jsx | 4 ++-- src/hooks/useProject.js | 4 ++-- src/utils/ResizableWithHandle.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx b/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx index ef640237b..fe76de2da 100644 --- a/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx +++ b/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx @@ -38,8 +38,8 @@ const OutputViewToggle = () => { isMobile ? null : isSplitView - ? t("outputViewToggle.buttonTabLabel") - : t("outputViewToggle.buttonSplitLabel") + ? t("outputViewToggle.buttonTabLabel") + : t("outputViewToggle.buttonSplitLabel") } disabled={codeRunTriggered || drawTriggered} label={ diff --git a/src/components/Menus/Sidebar/Sidebar.jsx b/src/components/Menus/Sidebar/Sidebar.jsx index 994b5ed87..86037eef1 100644 --- a/src/components/Menus/Sidebar/Sidebar.jsx +++ b/src/components/Menus/Sidebar/Sidebar.jsx @@ -139,8 +139,8 @@ const Sidebar = ({ options = [], plugins = [], allowMobileView = true }) => { autoOpenPlugin ? autoOpenPlugin.name : instructionsEditable || instructionsSteps - ? "instructions" - : "file", + ? "instructions" + : "file", ); const hasInstructions = instructionsSteps && instructionsSteps.length > 0; diff --git a/src/hooks/useProject.js b/src/hooks/useProject.js index 98c110621..d67b279a9 100644 --- a/src/hooks/useProject.js +++ b/src/hooks/useProject.js @@ -30,7 +30,7 @@ export const useProject = ({ const [cachedProject, setCachedProject] = useState( getCachedProject(projectIdentifier), ); - const { i18n } = useTranslation(); + const { i18n, t } = useTranslation(); const dispatch = useDispatch(); const effectiveLocale = locale ?? i18n.language; @@ -107,7 +107,7 @@ export const useProject = ({ const data = { ...defaultPythonProject, - name: i18n.t("project.untitled", { + name: t("project.untitled", { lng: effectiveLocale, defaultValue: defaultPythonProject.name, }), diff --git a/src/utils/ResizableWithHandle.js b/src/utils/ResizableWithHandle.js index fd66d9a76..796f5f7e5 100644 --- a/src/utils/ResizableWithHandle.js +++ b/src/utils/ResizableWithHandle.js @@ -56,8 +56,8 @@ const ResizableWithHandle = (props) => { let handleComponent = ["right", "left"].includes(handleDirection) ? { [handleDirection]: } : ["top", "bottom"].includes(handleDirection) - ? { [handleDirection]: } - : {}; + ? { [handleDirection]: } + : {}; let handleWrapperClass = `resizable-with-handle__handle resizable-with-handle__handle--${handleDirection}`; From d61e1c923682a1dc56f7a617e719c2aa68ae376d Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:10:42 +0100 Subject: [PATCH 12/17] update the way is pulled locales to maintain translation - In order to have locales we need to make defaultProjects async, added helpers to de-ref async loading and wait for locales to be loaded before shown --- src/hooks/useProject.js | 33 ++++++++++++++++++++----------- src/hooks/useProject.test.js | 12 +++++++----- src/utils/defaultProjects.js | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/src/hooks/useProject.js b/src/hooks/useProject.js index d67b279a9..793b30ad5 100644 --- a/src/hooks/useProject.js +++ b/src/hooks/useProject.js @@ -2,7 +2,7 @@ import { useRef, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import { syncProject, setProject } from "../redux/EditorSlice"; -import { defaultPythonProject } from "../utils/defaultProjects"; +import { createDefaultPythonProject } from "../utils/defaultProjects"; import { useTranslation } from "react-i18next"; export const useProject = ({ @@ -30,7 +30,7 @@ export const useProject = ({ const [cachedProject, setCachedProject] = useState( getCachedProject(projectIdentifier), ); - const { i18n, t } = useTranslation(); + const { i18n } = useTranslation(); const dispatch = useDispatch(); const effectiveLocale = locale ?? i18n.language; @@ -43,7 +43,13 @@ export const useProject = ({ }, [projectIdentifier]); useEffect(() => { - if (!loadRemix) { + let didUnmount = false; + + const loadProjectData = async () => { + if (loadRemix) { + return; + } + const is_cached_saved_project = projectIdentifier && cachedProject && @@ -105,15 +111,20 @@ export const useProject = ({ return; } - const data = { - ...defaultPythonProject, - name: t("project.untitled", { - lng: effectiveLocale, - defaultValue: defaultPythonProject.name, - }), - }; + const data = await createDefaultPythonProject(effectiveLocale); + + if (didUnmount) { + return; + } + dispatch(setProject(data)); - } + }; + + void loadProjectData(); + + return () => { + didUnmount = true; + }; }, [ code, projectIdentifier, diff --git a/src/hooks/useProject.test.js b/src/hooks/useProject.test.js index 7198e4aaf..b60a4bb13 100644 --- a/src/hooks/useProject.test.js +++ b/src/hooks/useProject.test.js @@ -57,11 +57,13 @@ describe("When not embedded", () => { test("If no identifier uses default python project", () => { renderHook(() => useProject({}), { wrapper }); - expect(setProject).toHaveBeenCalledWith( - expect.objectContaining({ - ...defaultPythonProject, - name: "project.untitled", - }), + return waitFor(() => + expect(setProject).toHaveBeenCalledWith( + expect.objectContaining({ + ...defaultPythonProject, + name: "project.untitled", + }), + ), ); }); diff --git a/src/utils/defaultProjects.js b/src/utils/defaultProjects.js index dcc9d9e01..ec703496d 100644 --- a/src/utils/defaultProjects.js +++ b/src/utils/defaultProjects.js @@ -1,3 +1,5 @@ +import i18n from "./i18n"; + const UNTITLED_PROJECT_NAME = "Untitled project"; export const defaultPythonProject = { @@ -25,3 +27,39 @@ export const DEFAULT_PROJECTS = { python: defaultPythonProject, html: defaultHtmlProject, }; + +export const createDefaultPythonProject = async (locale = i18n.language) => { + try { + if (locale && i18n.resolvedLanguage !== locale) { + await i18n.changeLanguage?.(locale); + } + } catch { + // Fall back to the default untitled name if locale files fail. + } + + return { + ...defaultPythonProject, + name: i18n.t("project.untitled", { + lng: locale, + defaultValue: UNTITLED_PROJECT_NAME, + }), + }; +}; + +export const createDefaultHtmlProject = async (locale = i18n.language) => { + try { + if (locale && i18n.resolvedLanguage !== locale) { + await i18n.changeLanguage?.(locale); + } + } catch { + // Fall back to the default untitled name if locale files fail. + } + + return { + ...defaultHtmlProject, + name: i18n.t("project.untitled", { + lng: locale, + defaultValue: UNTITLED_PROJECT_NAME, + }), + }; +}; From aa9fcb81b0824393968e942c39cb7083919551d8 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:25:44 +0100 Subject: [PATCH 13/17] simplify dedupe files --- src/utils/dedupeDesignSystemWarnings.js | 16 +-- src/utils/dedupeScratchWarnings.js | 129 ++++++++++-------------- 2 files changed, 64 insertions(+), 81 deletions(-) diff --git a/src/utils/dedupeDesignSystemWarnings.js b/src/utils/dedupeDesignSystemWarnings.js index e275eef45..6cdc23985 100644 --- a/src/utils/dedupeDesignSystemWarnings.js +++ b/src/utils/dedupeDesignSystemWarnings.js @@ -3,26 +3,26 @@ const designSystemIconWarning = "DEPRECATED: icons as React elements will not be supported in future releases"; const dedupeDesignSystemWarnings = () => { - if (process.env.NODE_ENV === "production" || typeof window !== "object") { - return; - } - - if (window[designSystemWarningsKey]) { + if ( + process.env.NODE_ENV === "production" || + typeof window !== "object" || + window[designSystemWarningsKey] + ) { return; } window[designSystemWarningsKey] = true; const originalWarn = console.warn.bind(console); - let hasShownDesignSystemIconWarning = false; + let didWarnIcon = false; console.warn = (...args) => { if (args[0] === designSystemIconWarning) { - if (hasShownDesignSystemIconWarning) { + if (didWarnIcon) { return; } - hasShownDesignSystemIconWarning = true; + didWarnIcon = true; } originalWarn(...args); diff --git a/src/utils/dedupeScratchWarnings.js b/src/utils/dedupeScratchWarnings.js index 49e4802d2..168361b08 100644 --- a/src/utils/dedupeScratchWarnings.js +++ b/src/utils/dedupeScratchWarnings.js @@ -1,96 +1,79 @@ -const scratchConsoleBrokerKey = "__scratchConsoleBrokerInstalled"; +const scratchWarningsKey = "__scratchWarningsDeduped"; -const scratchWarningMatchers = [ - { - method: "error", - needle: "Support for defaultProps will be removed", - id: "react-default-props", - summary: +const scratchWarningMatchers = { + error: [ + [ + "Support for defaultProps will be removed", "React 18 compatibility warnings about function-component defaultProps", - }, - { - method: "error", - needle: "React does not recognize the `%s` prop on a DOM element", - id: "react-dom-props", - summary: "React warnings about scratch-editor props leaking onto DOM nodes", - }, - { - method: "error", - needle: "Unknown event handler property `%s`", - id: "react-event-props", - summary: "React warnings about unsupported event handler props", - }, - { - method: "warn", - needle: "componentWillMount has been renamed", - id: "react-component-will-mount", - summary: "React warnings about legacy componentWillMount usage", - }, - { - method: "warn", - needle: "componentWillReceiveProps has been renamed", - id: "react-component-will-receive-props", - summary: "React warnings about legacy componentWillReceiveProps usage", - }, - { - method: "error", - needle: "findDOMNode is deprecated", - id: "react-find-dom-node", - summary: "React warnings about deprecated findDOMNode usage", - }, -]; - -const getScratchConsoleCategory = (method, args) => { - const message = typeof args[0] === "string" ? args[0] : ""; - const matcher = scratchWarningMatchers.find( - (candidate) => - candidate.method === method && message.includes(candidate.needle), - ); - - return matcher || null; + ], + [ + "React does not recognize the `%s` prop on a DOM element", + "React warnings about scratch-editor props leaking onto DOM nodes", + ], + [ + "Unknown event handler property `%s`", + "React warnings about unsupported event handler props", + ], + [ + "findDOMNode is deprecated", + "React warnings about deprecated findDOMNode usage", + ], + ], + warn: [ + [ + "componentWillMount has been renamed", + "React warnings about legacy componentWillMount usage", + ], + [ + "componentWillReceiveProps has been renamed", + "React warnings about legacy componentWillReceiveProps usage", + ], + ], }; const dedupeScratchWarnings = () => { - if (process.env.NODE_ENV === "production" || typeof window !== "object") { - return; - } - - if (window[scratchConsoleBrokerKey]) { + if ( + process.env.NODE_ENV === "production" || + typeof window !== "object" || + window[scratchWarningsKey] + ) { return; } - window[scratchConsoleBrokerKey] = true; + window[scratchWarningsKey] = true; - const seenCategories = new Set(); - const originalError = console.error.bind(console); + const seenSummaries = new Set(); const originalWarn = console.warn.bind(console); - const emitCategorySummary = (category) => { - if (seenCategories.has(category.id)) { - return; - } + const wrapConsoleMethod = (method) => { + const originalMethod = console[method].bind(console); - seenCategories.add(category.id); - originalWarn( - `[scratch-editor] emitted ${category.summary}. Further duplicates suppressed.`, - ); - }; - - const wrapConsoleMethod = (method, originalMethod) => { return (...args) => { - const category = getScratchConsoleCategory(method, args); + const message = typeof args[0] === "string" ? args[0] : ""; + const summary = scratchWarningMatchers[method]?.find(([needle]) => + message.includes(needle), + ); + + if (!summary) { + originalMethod(...args); + return; + } + + const [, text] = summary; - if (category) { - emitCategorySummary(category); + if (seenSummaries.has(text)) { return; } - originalMethod(...args); + seenSummaries.add(text); + originalWarn( + `[scratch-editor] emitted ${text}. Further duplicates suppressed.`, + ); }; }; - console.error = wrapConsoleMethod("error", originalError); - console.warn = wrapConsoleMethod("warn", originalWarn); + console.error = wrapConsoleMethod("error"); + console.warn = wrapConsoleMethod("warn"); }; export default dedupeScratchWarnings; From 0e7f5bf9c2f4d023d6f14969b2e1a56efe74a043 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:32:11 +0100 Subject: [PATCH 14/17] lint --- .../Editor/Runners/PythonRunner/OutputViewToggle.jsx | 4 ++-- src/components/Menus/Sidebar/Sidebar.jsx | 4 ++-- src/utils/ResizableWithHandle.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx b/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx index fe76de2da..ef640237b 100644 --- a/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx +++ b/src/components/Editor/Runners/PythonRunner/OutputViewToggle.jsx @@ -38,8 +38,8 @@ const OutputViewToggle = () => { isMobile ? null : isSplitView - ? t("outputViewToggle.buttonTabLabel") - : t("outputViewToggle.buttonSplitLabel") + ? t("outputViewToggle.buttonTabLabel") + : t("outputViewToggle.buttonSplitLabel") } disabled={codeRunTriggered || drawTriggered} label={ diff --git a/src/components/Menus/Sidebar/Sidebar.jsx b/src/components/Menus/Sidebar/Sidebar.jsx index 86037eef1..994b5ed87 100644 --- a/src/components/Menus/Sidebar/Sidebar.jsx +++ b/src/components/Menus/Sidebar/Sidebar.jsx @@ -139,8 +139,8 @@ const Sidebar = ({ options = [], plugins = [], allowMobileView = true }) => { autoOpenPlugin ? autoOpenPlugin.name : instructionsEditable || instructionsSteps - ? "instructions" - : "file", + ? "instructions" + : "file", ); const hasInstructions = instructionsSteps && instructionsSteps.length > 0; diff --git a/src/utils/ResizableWithHandle.js b/src/utils/ResizableWithHandle.js index 796f5f7e5..fd66d9a76 100644 --- a/src/utils/ResizableWithHandle.js +++ b/src/utils/ResizableWithHandle.js @@ -56,8 +56,8 @@ const ResizableWithHandle = (props) => { let handleComponent = ["right", "left"].includes(handleDirection) ? { [handleDirection]: } : ["top", "bottom"].includes(handleDirection) - ? { [handleDirection]: } - : {}; + ? { [handleDirection]: } + : {}; let handleWrapperClass = `resizable-with-handle__handle resizable-with-handle__handle--${handleDirection}`; From 0b1136d80adac0d2f6095d34fa5c96c831fcf418 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:40:10 +0100 Subject: [PATCH 15/17] copilot comments to invert the issue --- src/containers/WebComponentLoader.jsx | 4 +++- src/utils/dedupeDesignSystemWarnings.js | 2 +- src/utils/dedupeScratchWarnings.js | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/containers/WebComponentLoader.jsx b/src/containers/WebComponentLoader.jsx index 5f6e2d30a..b452ccb29 100644 --- a/src/containers/WebComponentLoader.jsx +++ b/src/containers/WebComponentLoader.jsx @@ -39,7 +39,9 @@ const TOAST_CONTAINER_DEFAULTS = { // react-toastify v8 uses defaultProps on a function component, which React // warns about in development. We pass the same defaults explicitly instead. // We should upgrade to version 10 in a different commit, this removes the warning -ToastContainer.defaultProps = undefined; +if (process.env.NODE_ENV === "development") { + ToastContainer.defaultProps = undefined; +} const WebComponentLoader = (props) => { const { diff --git a/src/utils/dedupeDesignSystemWarnings.js b/src/utils/dedupeDesignSystemWarnings.js index 6cdc23985..0f8dfda7a 100644 --- a/src/utils/dedupeDesignSystemWarnings.js +++ b/src/utils/dedupeDesignSystemWarnings.js @@ -4,7 +4,7 @@ const designSystemIconWarning = const dedupeDesignSystemWarnings = () => { if ( - process.env.NODE_ENV === "production" || + process.env.NODE_ENV !== "development" || typeof window !== "object" || window[designSystemWarningsKey] ) { diff --git a/src/utils/dedupeScratchWarnings.js b/src/utils/dedupeScratchWarnings.js index 168361b08..5a372ef1b 100644 --- a/src/utils/dedupeScratchWarnings.js +++ b/src/utils/dedupeScratchWarnings.js @@ -33,7 +33,7 @@ const scratchWarningMatchers = { const dedupeScratchWarnings = () => { if ( - process.env.NODE_ENV === "production" || + process.env.NODE_ENV !== "development" || typeof window !== "object" || window[scratchWarningsKey] ) { From ec5d060046014ac05f95a464725333080e083358 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:35:15 +0100 Subject: [PATCH 16/17] remove webpack console, probably not needed after prop changes --- src/components/ScratchEditor/ScratchIntegrationHOC.jsx | 4 ---- src/web-component.html | 3 --- 2 files changed, 7 deletions(-) diff --git a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx b/src/components/ScratchEditor/ScratchIntegrationHOC.jsx index 927560748..1ff3938d6 100644 --- a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx +++ b/src/components/ScratchEditor/ScratchIntegrationHOC.jsx @@ -43,10 +43,6 @@ const ScratchIntegrationHOC = function (WrappedComponent) { return; } - if (event.data?.type === "webpackOk") { - return; - } - switch (event.data.type) { case "scratch-gui-download": this.handleDownload(event); diff --git a/src/web-component.html b/src/web-component.html index 4ccd277d4..0611083eb 100644 --- a/src/web-component.html +++ b/src/web-component.html @@ -77,13 +77,10 @@ // subscribe to the 'codeChanged' custom event which is pushed by the project react component document.addEventListener("editor-codeChanged", function (e) { - console.log("listener in index html"); const code = webComp.editorCode; - console.log(code); }); document.addEventListener("editor-runCompleted", (e) => { - console.log(e.detail); document.getElementById("results").innerText = JSON.stringify(e.detail); }); From bac78c5bb202c165055c15713d4ac0c4ec4234c3 Mon Sep 17 00:00:00 2001 From: abcampo-iry <261805581+abcampo-iry@users.noreply.github.com> Date: Tue, 17 Mar 2026 16:26:32 +0100 Subject: [PATCH 17/17] re-enable message --- src/components/ScratchEditor/ScratchIntegrationHOC.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx b/src/components/ScratchEditor/ScratchIntegrationHOC.jsx index 1ff3938d6..338ee5fcb 100644 --- a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx +++ b/src/components/ScratchEditor/ScratchIntegrationHOC.jsx @@ -42,6 +42,9 @@ const ScratchIntegrationHOC = function (WrappedComponent) { ); return; } + if (event.data?.type === "webpackOk") { + return; + } switch (event.data.type) { case "scratch-gui-download":