From 04f301e583ddad9a8af8bca5da88b65edb7be83a Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:43:50 +0100 Subject: [PATCH 01/15] Move Scratch code into separate directory I've chosen to follow the default vite monorepo structure with sub-directories for each app. I've not modified the files to make the diff easier to see. I'll add additional commits to fix the imports and set up Vite for the new app. --- .../scratch-frame/src}/ScratchEditor.jsx | 0 .../scratch-frame/src}/ScratchEditor.test.jsx | 0 .../scratch-frame/src}/ScratchIntegrationHOC.jsx | 0 .../scratch-frame/src}/ScratchIntegrationHOC.test.jsx | 0 .../scratch-frame/src}/WrappedScratchGui.jsx | 0 {src => apps/scratch-frame/src}/scratch.html | 0 {src => apps/scratch-frame/src}/scratch.jsx | 0 {src => apps/scratch-frame/src}/scratch.test.js | 0 .../scratch-frame/src}/stylesheets/Scratch.scss | 0 .../scratch-frame/src}/utils/dedupeScratchWarnings.js | 0 .../scratch-frame/src/utils}/events.js | 0 apps/scratch-frame/src/utils/iframeUtils.js | 6 ++++++ {src => apps/scratch-frame/src}/utils/scratchProjectSave.js | 0 .../scratch-frame/src}/utils/scratchProjectSave.test.js | 0 14 files changed, 6 insertions(+) rename {src/components/ScratchEditor => apps/scratch-frame/src}/ScratchEditor.jsx (100%) rename {src/components/ScratchEditor => apps/scratch-frame/src}/ScratchEditor.test.jsx (100%) rename {src/components/ScratchEditor => apps/scratch-frame/src}/ScratchIntegrationHOC.jsx (100%) rename {src/components/ScratchEditor => apps/scratch-frame/src}/ScratchIntegrationHOC.test.jsx (100%) rename {src/components/ScratchEditor => apps/scratch-frame/src}/WrappedScratchGui.jsx (100%) rename {src => apps/scratch-frame/src}/scratch.html (100%) rename {src => apps/scratch-frame/src}/scratch.jsx (100%) rename {src => apps/scratch-frame/src}/scratch.test.js (100%) rename {src/assets => apps/scratch-frame/src}/stylesheets/Scratch.scss (100%) rename {src => apps/scratch-frame/src}/utils/dedupeScratchWarnings.js (100%) rename {src/components/ScratchEditor => apps/scratch-frame/src/utils}/events.js (100%) create mode 100644 apps/scratch-frame/src/utils/iframeUtils.js rename {src => apps/scratch-frame/src}/utils/scratchProjectSave.js (100%) rename {src => apps/scratch-frame/src}/utils/scratchProjectSave.test.js (100%) diff --git a/src/components/ScratchEditor/ScratchEditor.jsx b/apps/scratch-frame/src/ScratchEditor.jsx similarity index 100% rename from src/components/ScratchEditor/ScratchEditor.jsx rename to apps/scratch-frame/src/ScratchEditor.jsx diff --git a/src/components/ScratchEditor/ScratchEditor.test.jsx b/apps/scratch-frame/src/ScratchEditor.test.jsx similarity index 100% rename from src/components/ScratchEditor/ScratchEditor.test.jsx rename to apps/scratch-frame/src/ScratchEditor.test.jsx diff --git a/src/components/ScratchEditor/ScratchIntegrationHOC.jsx b/apps/scratch-frame/src/ScratchIntegrationHOC.jsx similarity index 100% rename from src/components/ScratchEditor/ScratchIntegrationHOC.jsx rename to apps/scratch-frame/src/ScratchIntegrationHOC.jsx diff --git a/src/components/ScratchEditor/ScratchIntegrationHOC.test.jsx b/apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx similarity index 100% rename from src/components/ScratchEditor/ScratchIntegrationHOC.test.jsx rename to apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx diff --git a/src/components/ScratchEditor/WrappedScratchGui.jsx b/apps/scratch-frame/src/WrappedScratchGui.jsx similarity index 100% rename from src/components/ScratchEditor/WrappedScratchGui.jsx rename to apps/scratch-frame/src/WrappedScratchGui.jsx diff --git a/src/scratch.html b/apps/scratch-frame/src/scratch.html similarity index 100% rename from src/scratch.html rename to apps/scratch-frame/src/scratch.html diff --git a/src/scratch.jsx b/apps/scratch-frame/src/scratch.jsx similarity index 100% rename from src/scratch.jsx rename to apps/scratch-frame/src/scratch.jsx diff --git a/src/scratch.test.js b/apps/scratch-frame/src/scratch.test.js similarity index 100% rename from src/scratch.test.js rename to apps/scratch-frame/src/scratch.test.js diff --git a/src/assets/stylesheets/Scratch.scss b/apps/scratch-frame/src/stylesheets/Scratch.scss similarity index 100% rename from src/assets/stylesheets/Scratch.scss rename to apps/scratch-frame/src/stylesheets/Scratch.scss diff --git a/src/utils/dedupeScratchWarnings.js b/apps/scratch-frame/src/utils/dedupeScratchWarnings.js similarity index 100% rename from src/utils/dedupeScratchWarnings.js rename to apps/scratch-frame/src/utils/dedupeScratchWarnings.js diff --git a/src/components/ScratchEditor/events.js b/apps/scratch-frame/src/utils/events.js similarity index 100% rename from src/components/ScratchEditor/events.js rename to apps/scratch-frame/src/utils/events.js diff --git a/apps/scratch-frame/src/utils/iframeUtils.js b/apps/scratch-frame/src/utils/iframeUtils.js new file mode 100644 index 000000000..42024bd41 --- /dev/null +++ b/apps/scratch-frame/src/utils/iframeUtils.js @@ -0,0 +1,6 @@ +export function allowedIframeHost(origin) { + const allowedHosts = process.env.REACT_APP_ALLOWED_IFRAME_ORIGINS + ? process.env.REACT_APP_ALLOWED_IFRAME_ORIGINS.split(",") + : []; + return process.env.NODE_ENV === "test" || allowedHosts.includes(origin); +} diff --git a/src/utils/scratchProjectSave.js b/apps/scratch-frame/src/utils/scratchProjectSave.js similarity index 100% rename from src/utils/scratchProjectSave.js rename to apps/scratch-frame/src/utils/scratchProjectSave.js diff --git a/src/utils/scratchProjectSave.test.js b/apps/scratch-frame/src/utils/scratchProjectSave.test.js similarity index 100% rename from src/utils/scratchProjectSave.test.js rename to apps/scratch-frame/src/utils/scratchProjectSave.test.js From 63bd2bd5349444fd9dafb686e5b1698e7f2667e9 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Mon, 29 Jun 2026 13:45:40 +0100 Subject: [PATCH 02/15] Fix imports after moving Scratch files --- apps/scratch-frame/src/ScratchEditor.jsx | 4 ++-- apps/scratch-frame/src/ScratchEditor.test.jsx | 2 +- apps/scratch-frame/src/ScratchIntegrationHOC.jsx | 4 ++-- apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx | 4 ++-- apps/scratch-frame/src/scratch.jsx | 9 +++------ apps/scratch-frame/src/scratch.test.js | 4 ++-- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/apps/scratch-frame/src/ScratchEditor.jsx b/apps/scratch-frame/src/ScratchEditor.jsx index 8ce149903..dea12ac69 100644 --- a/apps/scratch-frame/src/ScratchEditor.jsx +++ b/apps/scratch-frame/src/ScratchEditor.jsx @@ -1,8 +1,8 @@ -import scratchProjectSave from "../../utils/scratchProjectSave.js"; +import scratchProjectSave from "./utils/scratchProjectSave.js"; import { useCallback, useRef, useEffect, useState } from "react"; import WrapperdScratchGui from "./WrappedScratchGui.jsx"; -import { postScratchGuiEvent, allowedParentOrigin } from "./events.js"; +import { postScratchGuiEvent, allowedParentOrigin } from "./utils/events.js"; /** Scratch library picker assets (not project save/load — those use editor-api). */ export const SCRATCH_LIBRARY_ASSET_URL_TEMPLATE = diff --git a/apps/scratch-frame/src/ScratchEditor.test.jsx b/apps/scratch-frame/src/ScratchEditor.test.jsx index 132572db2..9b2b9bb8b 100644 --- a/apps/scratch-frame/src/ScratchEditor.test.jsx +++ b/apps/scratch-frame/src/ScratchEditor.test.jsx @@ -6,7 +6,7 @@ import ScratchEditor, { const mockWrappedScratchGui = jest.fn(); const mockScratchProjectSave = jest.fn(); -jest.mock("../../utils/scratchProjectSave.js", () => ({ +jest.mock("./utils/scratchProjectSave.js", () => ({ __esModule: true, default: (params) => mockScratchProjectSave(params), })); diff --git a/apps/scratch-frame/src/ScratchIntegrationHOC.jsx b/apps/scratch-frame/src/ScratchIntegrationHOC.jsx index f28a1dca4..d2de05604 100644 --- a/apps/scratch-frame/src/ScratchIntegrationHOC.jsx +++ b/apps/scratch-frame/src/ScratchIntegrationHOC.jsx @@ -7,8 +7,8 @@ import { manualUpdateProject, setStageSize, } from "@RaspberryPiFoundation/scratch-gui"; -import { allowedIframeHost } from "../../utils/iframeUtils"; -import { postScratchGuiEvent } from "./events.js"; +import { allowedIframeHost } from "./utils/iframeUtils"; +import { postScratchGuiEvent } from "./utils/events.js"; const ScratchIntegrationHOC = function (WrappedComponent) { class ScratchIntegrationComponent extends React.Component { diff --git a/apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx b/apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx index a2a292982..eec3a5a04 100644 --- a/apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx +++ b/apps/scratch-frame/src/ScratchIntegrationHOC.test.jsx @@ -4,7 +4,7 @@ const { Provider } = require("react-redux"); const configureStore = require("redux-mock-store").default; jest.mock("file-saver", () => ({ saveAs: jest.fn() })); -jest.mock("./events.js", () => ({ postScratchGuiEvent: jest.fn() })); +jest.mock("./utils/events.js", () => ({ postScratchGuiEvent: jest.fn() })); jest.mock("@RaspberryPiFoundation/scratch-gui", () => ({ remixProject: () => ({ type: "remix" }), manualUpdateProject: () => ({ type: "manualUpdate" }), @@ -12,7 +12,7 @@ jest.mock("@RaspberryPiFoundation/scratch-gui", () => ({ })); const ScratchIntegrationHOC = require("./ScratchIntegrationHOC").default; -const { postScratchGuiEvent } = require("./events.js"); +const { postScratchGuiEvent } = require("./utils/events.js"); describe("ScratchIntegrationHOC", () => { const mockSaveProjectSb3 = jest.fn(); diff --git a/apps/scratch-frame/src/scratch.jsx b/apps/scratch-frame/src/scratch.jsx index f93b9c8d0..3aaeed882 100644 --- a/apps/scratch-frame/src/scratch.jsx +++ b/apps/scratch-frame/src/scratch.jsx @@ -3,12 +3,9 @@ import { createRoot } from "react-dom/client"; import process from "process"; import dedupeScratchWarnings from "./utils/dedupeScratchWarnings.js"; -import ScratchStyles from "./assets/stylesheets/Scratch.scss"; -import ScratchEditor from "./components/ScratchEditor/ScratchEditor.jsx"; -import { - postScratchGuiEvent, - allowedParentOrigin, -} from "./components/ScratchEditor/events.js"; +import ScratchStyles from "./stylesheets/Scratch.scss"; +import ScratchEditor from "./ScratchEditor.jsx"; +import { postScratchGuiEvent, allowedParentOrigin } from "./utils/events.js"; dedupeScratchWarnings(); const appTarget = document.getElementById("app"); diff --git a/apps/scratch-frame/src/scratch.test.js b/apps/scratch-frame/src/scratch.test.js index 0a7f601d3..b13848dc3 100644 --- a/apps/scratch-frame/src/scratch.test.js +++ b/apps/scratch-frame/src/scratch.test.js @@ -1,6 +1,6 @@ jest.mock("./utils/dedupeScratchWarnings.js", () => jest.fn()); -jest.mock("./assets/stylesheets/Scratch.scss", () => ""); -jest.mock("./components/ScratchEditor/WrappedScratchGui.jsx", () => (props) => { +jest.mock("./stylesheets/Scratch.scss", () => ""); +jest.mock("./WrappedScratchGui.jsx", () => (props) => { return null; }); From e9e6cb6294e68903b7e9f76e180d9e6323a22aa2 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Tue, 30 Jun 2026 14:45:48 +0100 Subject: [PATCH 03/15] Setup Vite for scratch frame app I thought this was a good place to introduce Vite as we need a way to build this app and there's a small amount of code to migrate. I've made the projects use yarn workspaces for now as I think this is a simpler first step, but we could move away from this later. --- apps/scratch-frame/README.md | 1 + apps/scratch-frame/package.json | 25 + apps/scratch-frame/{src => }/scratch.html | 12 +- apps/scratch-frame/src/ScratchEditor.jsx | 1 + apps/scratch-frame/src/bootstrap.jsx | 34 ++ apps/scratch-frame/vite.config.js | 99 ++++ package.json | 3 + webpack.config.js | 141 +----- yarn.lock | 563 +++++++++++++++++++++- 9 files changed, 708 insertions(+), 171 deletions(-) create mode 100644 apps/scratch-frame/README.md create mode 100644 apps/scratch-frame/package.json rename apps/scratch-frame/{src => }/scratch.html (80%) create mode 100644 apps/scratch-frame/src/bootstrap.jsx create mode 100644 apps/scratch-frame/vite.config.js diff --git a/apps/scratch-frame/README.md b/apps/scratch-frame/README.md new file mode 100644 index 000000000..ed463df65 --- /dev/null +++ b/apps/scratch-frame/README.md @@ -0,0 +1 @@ +# scratch-frame diff --git a/apps/scratch-frame/package.json b/apps/scratch-frame/package.json new file mode 100644 index 000000000..65d08d415 --- /dev/null +++ b/apps/scratch-frame/package.json @@ -0,0 +1,25 @@ +{ + "name": "@raspberrypifoundation/scratch-frame", + "private": true, + "dependencies": { + "@RaspberryPiFoundation/scratch-gui": "13.7.3-code-classroom.20260522151700", + "@vitejs/plugin-react": "^6.0.2", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-redux": "^8.1.3", + "redux": "^4.2.1", + "vite": "^8.0.16", + "vite-plugin-static-copy": "^4.1.1" + }, + "scripts": { + "dev": "vite", + "start": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@testing-library/react": "14.3.1", + "redux-mock-store": "^1.5.4" + }, + "packageManager": "yarn@4.12.0" +} diff --git a/apps/scratch-frame/src/scratch.html b/apps/scratch-frame/scratch.html similarity index 80% rename from apps/scratch-frame/src/scratch.html rename to apps/scratch-frame/scratch.html index 091c4ea3b..8c1e8cc91 100644 --- a/apps/scratch-frame/src/scratch.html +++ b/apps/scratch-frame/scratch.html @@ -73,16 +73,6 @@