scratch-gui

Code conventions

The repository holds two React apps that grew up separately: the editor (inherited from Scratch GUI and TurboWarp) and the community site (src/community). They follow different conventions. Match the code around you rather than mixing them.

Editor (src/components, src/containers, src/lib, src/playground)

Community site (src/community)

Modules and TypeScript (src/modules)

New shared code goes in src/modules/<name>/, written in TypeScript. A module is a folder with an index.ts that is its only public entry point. Everything else in the folder is private to it.

Current modules:

Module What it owns
platform-api The MistWarp API transport: createPlatformApi gives request, requestRaw, upload and logout, plus the session (stored token, session providers, the Rotur validator exchange) and the GET response cache. Error construction, analytics and sign-in side effects are passed in. src/lib/community/api.js composes it for the app.
storage local and session stores that never throw: readString, readJSON (typed only when given a type guard, unknown otherwise), readBoolean, the matching writes, remove, keys, and isAvailable for code that must tell blocked storage from a missing key. createStore wraps any other Storage, such as one passed in by a caller. Also reads the legacy tw:addons settings blob.
project-session Editor state that belongs to the open project, kept beside the VM rather than on it. projectSession(vm) returns its record (unsaved imported changes, which git remotes may be pushed, history hydration, whether the last load may be trusted, and whether the user trusted the project with Load all). detachProjectSession(vm) detaches the workspace. setLoadHandoff and takeLoadHandoff pass the release, rollback and history callbacks from the project fetcher to the VM manager.
blocks-registry The loaded scratch-blocks library and the editor’s active workspace. setScratchBlocks, getScratchBlocks and whenScratchBlocks (a promise for code that runs before the library loads), and setActiveWorkspace, getActiveWorkspace and onActiveWorkspace (called with the new workspace, or null when the blocks pane unmounts). containers/blocks.jsx is the only writer.
editor-layout The editor shell’s size rules: minimum editor pane and page sizes, the narrow layout breakpoint, and the stage panel clamps for resizing, dragging, keyboard steps, phone and player stages. Callers measure the DOM and pass numbers in.
paused-audio Silencing a runtime’s audio while it is paused: silenceAudioWhilePaused(runtime) follows RUNTIME_PAUSED and RUNTIME_UNPAUSED, sets audioEngine.paused and suspends or resumes the audio context. The debugger and the packager pause button use it beside the VM’s own pause.
project-metadata The MistWarp keys in a saved project’s meta: installProjectMetadata(vm) registers created, edited, agent and author with vm.registerProjectMetadata, and writes the other keys a loaded project had into its next save when the VM does not write them itself. State is kept per VM. setProjectAuthor sets the byline and getLoadedProjectMeta(vm) returns the open project’s meta.

Never add fields to the VM object. Lint rejects _mw* and __mw* fields on the VM and its runtime, apart from the ones scratch-vm defines itself; put project state in project-session, and install guards in a WeakSet or WeakMap in the file that installs them.

Don’t replace VM or runtime methods to run code around them. Use the extension points scratch-vm provides and keep the function they return to undo it: runtime.addStepListener({before, after}) for work around each frame, vm.setPaused and vm.isPaused with the RUNTIME_PAUSED and RUNTIME_UNPAUSED events for pausing, vm.addLoadProjectInterceptor to check or follow project loads, vm.registerProjectMetadata for extra meta keys, and vm.setProjectName for the title extensions see.

Code that needs the loaded library or the active workspace reads it from blocks-registry, not from globals. Lint rejects window.ScratchBlocks, window.Blockly, AddonHooks.blockly and AddonHooks.blocklyWorkspace outside containers/blocks.jsx, which still sets them for addons and userscripts.

Code outside src/modules/storage must not touch localStorage or sessionStorage directly, and lint enforces this across src.

Shared code

Components and state

Tests

Planned tooling changes

These need new packages, so they are follow-ups rather than current practice: React Testing Library in place of Enzyme, ESLint 9 with a flat config, Playwright in place of Selenium for the browser tests, a component workshop (Storybook or Ladle), and Mock Service Worker for API mocks in tests.