diff --git a/package-lock.json b/package-lock.json index 2f4b644a917..eed278feb7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,11 @@ "license": "AGPL-3.0-only", "dependencies": { "@microbit/microbit-universal-hex": "^0.2.2", + "@types/node": "^22.13.14", + "@webcontainer/api": "^1.5.3", + "abstract-syntax-tree": "^2.22.0", + "ace-builds": "^1.39.1", + "ace-linters": "^1.5.2", "arraybuffer-loader": "^1.0.6", "autoprefixer": "^9.0.1", "balance-text": "^3.3.1", @@ -25,6 +30,7 @@ "dapjs": "^2.3.0", "es6-object-assign": "^1.1.0", "fastestsmallesttextencoderdecoder": "^1.0.22", + "fs": "^0.0.1-security", "get-float-time-domain-data": "^0.1.0", "get-user-media-promise": "^1.1.4", "immutable": "^3.8.2", @@ -39,12 +45,14 @@ "minilog": "^3.1.0", "omggif": "^1.0.9", "papaparse": "^5.3.0", + "path": "^0.12.7", "postcss-import": "^12.0.0", "postcss-loader": "4.3.0", "postcss-simple-vars": "^5.0.1", "prop-types": "^15.5.10", "query-string": "^5.1.1", "raw-loader": "^4.0.0", + "react-ace": "^14.0.1", "react-contextmenu": "^2.9.4", "react-draggable": "^3.0.5", "react-ga": "^2.5.3", @@ -59,6 +67,7 @@ "react-virtualized": "^9.20.1", "redux": "^3.7.2", "redux-throttle": "^0.1.1", + "request": "^2.88.2", "scratch-audio": "^2.0.0", "scratch-blocks": "^1.1.6", "scratch-l10n": "^5.0.0", @@ -68,9 +77,12 @@ "scratch-storage": "^4.0.0", "scratch-svg-renderer": "^3.0.0", "scratch-vm": "^5.0.0", + "scratch-vm-local": "file:../scratch-vm-local", "startaudiocontext": "^1.2.1", "style-loader": "4.0.0", "to-style": "^1.3.3", + "ts-node": "^10.9.2", + "uuid": "^11.1.0", "wav-encoder": "^1.3.0", "xhr": "^2.5.0" }, @@ -2039,6 +2051,12 @@ "node": ">=v14" } }, + "node_modules/@commitlint/load/node_modules/@types/node": { + "version": "20.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", + "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", + "dev": true + }, "node_modules/@commitlint/message": { "version": "17.8.1", "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz", @@ -2156,7 +2174,6 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -2169,7 +2186,6 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -3270,28 +3286,24 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, "license": "MIT" }, "node_modules/@types/babel__core": { @@ -3522,10 +3534,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz", - "integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==", - "license": "MIT" + "version": "22.13.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.14.tgz", + "integrity": "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==", + "dependencies": { + "undici-types": "~6.20.0" + } }, "node_modules/@types/node-forge": { "version": "1.3.11", @@ -3897,6 +3911,11 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@webcontainer/api": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@webcontainer/api/-/api-1.5.3.tgz", + "integrity": "sha512-f6Oq3ohtSC5RYABhpN8aVOVHpcKvJ1fB1jjuvODTBU5u6BcroYEhphnrywdw8RO+2Vy5ekCdKe5D4dCMdMSrzA==" + }, "node_modules/@webpack-cli/configtest": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", @@ -3964,6 +3983,30 @@ "dev": true, "license": "ISC" }, + "node_modules/abstract-syntax-tree": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/abstract-syntax-tree/-/abstract-syntax-tree-2.22.0.tgz", + "integrity": "sha512-Px1YA1lvdQN/DGqyZ4rIp6LH8mEtRcyFYZw48cYQ/fK0r7QffIPkEV2ob3g804aS9OjUwuKoqhPTKb3kvZVhug==", + "dependencies": { + "ast-types": "0.14.2", + "astring": "^1.8.6", + "esquery": "^1.5.0", + "meriyah": "^4.4.0", + "pure-conditions": "^1.2.1", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=20.11.1" + } + }, + "node_modules/abstract-syntax-tree/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3988,6 +4031,21 @@ "node": ">= 0.6" } }, + "node_modules/ace-builds": { + "version": "1.39.1", + "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.39.1.tgz", + "integrity": "sha512-HcJbBzx8qY66t9gZo/sQu7pi0wO/CFLdYn1LxQO1WQTfIkMfyc7LRnBpsp/oNCSSU/LL83jXHN1fqyOTuIhUjg==" + }, + "node_modules/ace-linters": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ace-linters/-/ace-linters-1.5.2.tgz", + "integrity": "sha512-ZMDyQgZCjo29ZzR/qKm+BnnF+x6GRlVK8a4EDc1A96htr7Qnx/mgUHJEV0KoCTO1E4TKYEdwOJ/DCFUWpDIY2Q==", + "dependencies": { + "vscode-languageserver-protocol": "^3.17.5", + "vscode-languageserver-textdocument": "^1.0.8", + "vscode-languageserver-types": "^3.17.3" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -4037,7 +4095,6 @@ "version": "8.3.4", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, "license": "MIT", "dependencies": { "acorn": "^8.11.0" @@ -4255,7 +4312,6 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -4660,6 +4716,17 @@ "node": ">=0.10.0" } }, + "node_modules/ast-types": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/astral-regex": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", @@ -4670,6 +4737,14 @@ "node": ">=4" } }, + "node_modules/astring": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", + "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", + "bin": { + "astring": "bin/astring" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -7379,7 +7454,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, "license": "MIT" }, "node_modules/cross-fetch": { @@ -9686,7 +9760,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" @@ -10905,6 +10978,11 @@ ], "license": "MIT" }, + "node_modules/fs": { + "version": "0.0.1-security", + "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", + "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -17413,12 +17491,17 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead." + }, "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, "license": "MIT" }, "node_modules/lodash.isfunction": { @@ -17615,7 +17698,6 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, "license": "ISC" }, "node_modules/makeerror": { @@ -17921,6 +18003,14 @@ "node": ">= 8" } }, + "node_modules/meriyah": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-4.5.0.tgz", + "integrity": "sha512-Rbiu0QPIxTXgOXwiIpRVJfZRQ2FWyfzYrOGBs9SN5RbaXg1CN5ELn/plodwWwluX93yzc4qO/bNIen1ThGFCxw==", + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -22325,6 +22415,15 @@ "node": ">=0.10.0" } }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-browserify": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", @@ -22382,6 +22481,19 @@ "node": ">=8" } }, + "node_modules/path/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, + "node_modules/path/node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/pbkdf2": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", @@ -23224,6 +23336,11 @@ "dev": true, "license": "MIT" }, + "node_modules/pure-conditions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pure-conditions/-/pure-conditions-1.2.1.tgz", + "integrity": "sha512-MKk7sKQiR3Fe3bL/QedUZ1eVoNO0xpOCyiTGdVrK+4ZCDa9TgwNp6D/U1FthjhVGS9a857BS84WncvZvOYECUw==" + }, "node_modules/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -23584,6 +23701,22 @@ "node": ">=0.10.0" } }, + "node_modules/react-ace": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-14.0.1.tgz", + "integrity": "sha512-z6YAZ20PNf/FqmYEic//G/UK6uw0rn21g58ASgHJHl9rfE4nITQLqthr9rHMVQK4ezwohJbp2dGrZpkq979PYQ==", + "dependencies": { + "ace-builds": "^1.36.3", + "diff-match-patch": "^1.0.5", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-contextmenu": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/react-contextmenu/-/react-contextmenu-2.14.0.tgz", @@ -23834,6 +23967,14 @@ "react-dom": ">=16.0.0" } }, + "node_modules/react-tooltip/node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/react-virtualized": { "version": "9.22.6", "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.6.tgz", @@ -24540,7 +24681,6 @@ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -25555,10 +25695,9 @@ } }, "node_modules/scratch-svg-renderer": { - "version": "3.0.73", - "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-3.0.73.tgz", - "integrity": "sha512-1PTEXIwn0Y6Ib6dy0kSkdIXqVkt30TCfBGxQrJClaHrq1XGEBH/M92Hc2hSvwXRYLX30GrALT0o5SLWGJTw8EA==", - "license": "AGPL-3.0-only", + "version": "3.0.75", + "resolved": "https://registry.npmjs.org/scratch-svg-renderer/-/scratch-svg-renderer-3.0.75.tgz", + "integrity": "sha512-/vsDEBgcuxkhvx2YbgQLSS/yFclWCkwxyhUJ5t2D/+RZOkAY0Iv5oIp32I+bEkYe3pNNW1g55z7vi48jAYY47A==", "dependencies": { "base64-js": "^1.2.1", "base64-loader": "^1.0.0", @@ -25579,9 +25718,38 @@ "license": "BSD-3-Clause" }, "node_modules/scratch-vm": { - "version": "5.0.198", - "resolved": "https://registry.npmjs.org/scratch-vm/-/scratch-vm-5.0.198.tgz", - "integrity": "sha512-nH8hBHSGZY1C7/GDs4Bf2Bt6NRD2WaFB4XGYr8zB4Uj8koOmUknB+5bl0qjNHpO1LIef9vXhSzbY6DXJy37UQA==", + "version": "5.0.206", + "resolved": "file:../scratch-vm", + "license": "AGPL-3.0-only", + "dependencies": { + "@vernier/godirect": "^1.5.0", + "arraybuffer-loader": "^1.0.6", + "atob": "^2.1.2", + "btoa": "^1.2.1", + "buffer": "^6.0.3", + "canvas-toBlob": "^1.0.0", + "decode-html": "^2.0.0", + "diff-match-patch": "^1.0.4", + "format-message": "^6.2.1", + "htmlparser2": "^3.10.0", + "immutable": "^3.8.1", + "jszip": "^3.1.5", + "minilog": "^3.1.0", + "scratch-audio": "^2.0.0", + "scratch-parser": "^6.0.0", + "scratch-render": "^2.0.0", + "scratch-sb1-converter": "^2.0.0", + "scratch-storage": "^4.0.0", + "scratch-svg-renderer": "3.0.75", + "scratch-translate-extension-languages": "^1.0.0", + "text-encoding": "^0.7.0", + "uuid": "^8.3.2", + "web-worker": "^1.3.0" + } + }, + "node_modules/scratch-vm-local": { + "version": "5.0.206", + "resolved": "file:../scratch-vm-local", "license": "AGPL-3.0-only", "dependencies": { "@vernier/godirect": "^1.5.0", @@ -25602,13 +25770,44 @@ "scratch-render": "^2.0.0", "scratch-sb1-converter": "^2.0.0", "scratch-storage": "^4.0.0", - "scratch-svg-renderer": "3.0.73", + "scratch-svg-renderer": "3.0.75", "scratch-translate-extension-languages": "^1.0.0", "text-encoding": "^0.7.0", "uuid": "^8.3.2", "web-worker": "^1.3.0" } }, + "node_modules/scratch-vm-local/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/scratch-vm-local/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/scratch-vm/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -28516,8 +28715,6 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -28560,7 +28757,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -28767,7 +28963,6 @@ "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -28844,6 +29039,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", @@ -29298,19 +29498,21 @@ } }, "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "license": "MIT", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, "license": "MIT" }, "node_modules/validate-npm-package-license": { @@ -29360,6 +29562,33 @@ "dev": true, "license": "MIT" }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -30146,7 +30375,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" diff --git a/package.json b/package.json index 3c277f3d8d2..c1cc2139afb 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,11 @@ }, "dependencies": { "@microbit/microbit-universal-hex": "^0.2.2", + "@types/node": "^22.13.14", + "@webcontainer/api": "^1.5.3", + "abstract-syntax-tree": "^2.22.0", + "ace-builds": "^1.39.1", + "ace-linters": "^1.5.2", "arraybuffer-loader": "^1.0.6", "autoprefixer": "^9.0.1", "balance-text": "^3.3.1", @@ -48,6 +53,7 @@ "dapjs": "^2.3.0", "es6-object-assign": "^1.1.0", "fastestsmallesttextencoderdecoder": "^1.0.22", + "fs": "^0.0.1-security", "get-float-time-domain-data": "^0.1.0", "get-user-media-promise": "^1.1.4", "immutable": "^3.8.2", @@ -62,12 +68,14 @@ "minilog": "^3.1.0", "omggif": "^1.0.9", "papaparse": "^5.3.0", + "path": "^0.12.7", "postcss-import": "^12.0.0", "postcss-loader": "4.3.0", "postcss-simple-vars": "^5.0.1", "prop-types": "^15.5.10", "query-string": "^5.1.1", "raw-loader": "^4.0.0", + "react-ace": "^14.0.1", "react-contextmenu": "^2.9.4", "react-draggable": "^3.0.5", "react-ga": "^2.5.3", @@ -82,6 +90,7 @@ "react-virtualized": "^9.20.1", "redux": "^3.7.2", "redux-throttle": "^0.1.1", + "request": "^2.88.2", "scratch-audio": "^2.0.0", "scratch-blocks": "^1.1.6", "scratch-l10n": "^5.0.0", @@ -91,9 +100,12 @@ "scratch-storage": "^4.0.0", "scratch-svg-renderer": "^3.0.0", "scratch-vm": "^5.0.0", + "scratch-vm-local": "file:../scratch-vm-local", "startaudiocontext": "^1.2.1", "style-loader": "4.0.0", "to-style": "^1.3.3", + "ts-node": "^10.9.2", + "uuid": "^11.1.0", "wav-encoder": "^1.3.0", "xhr": "^2.5.0" }, diff --git a/src/components/gui/gui.jsx b/src/components/gui/gui.jsx index f2d0a8e8f73..851deee2f0b 100644 --- a/src/components/gui/gui.jsx +++ b/src/components/gui/gui.jsx @@ -1,11 +1,10 @@ import classNames from 'classnames'; import omit from 'lodash.omit'; import PropTypes from 'prop-types'; -import React from 'react'; -import {defineMessages, FormattedMessage, injectIntl, intlShape} from 'react-intl'; -import {connect} from 'react-redux'; +import { defineMessages, FormattedMessage, injectIntl, intlShape } from 'react-intl'; +import { connect } from 'react-redux'; import MediaQuery from 'react-responsive'; -import {Tab, Tabs, TabList, TabPanel} from 'react-tabs'; +import { Tab, Tabs, TabList, TabPanel } from 'react-tabs'; import tabStyles from 'react-tabs/style/react-tabs.css'; import VM from 'scratch-vm'; import Renderer from 'scratch-render'; @@ -31,9 +30,9 @@ import DragLayer from '../../containers/drag-layer.jsx'; import ConnectionModal from '../../containers/connection-modal.jsx'; import TelemetryModal from '../telemetry-modal/telemetry-modal.jsx'; -import layout, {STAGE_SIZE_MODES} from '../../lib/layout-constants'; -import {resolveStageSize} from '../../lib/screen-utils'; -import {themeMap} from '../../lib/themes'; +import layout, { STAGE_SIZE_MODES } from '../../lib/layout-constants'; +import { resolveStageSize } from '../../lib/screen-utils'; +import { themeMap } from '../../lib/themes'; import styles from './gui.css'; import addExtensionIcon from './icon--extensions.svg'; @@ -41,443 +40,575 @@ import codeIcon from './icon--code.svg'; import costumesIcon from './icon--costumes.svg'; import soundsIcon from './icon--sounds.svg'; import DebugModal from '../debug-modal/debug-modal.jsx'; +import { useEffect } from 'react'; +import { WebContainer } from '@webcontainer/api'; + + +import React, { useState } from 'react'; +import AceEditor from 'react-ace'; +import 'ace-builds/src-noconflict/mode-javascript'; +import 'ace-builds/src-noconflict/theme-monokai'; +import 'ace-builds/webpack-resolver'; + +// import VM2 from 'scratch-vm-local'; + +// const { Runtime, Motion, Sprite, RenderedTarget } = VM2.EngineFunctions; + +// function TestMotion() { +// const rt = new Runtime(); +// const motion = new Motion(rt); +// const sprite = new Sprite(null, rt); +// const target = new RenderedTarget(sprite, rt); +// const util = { target }; + +// motion.goToXY({ X: 0.999999999, Y: 0.999999999 }, util); +// } + +function CodeEditor(props) { + const [files, setFiles] = useState({ + 'sprite1.js': ` + export function run (ctx, target, MathUtil) { + ctx.on("KEY_PRESSED", function (key) { + if(key === "right arrow") { + const steps = 10; + const radians = MathUtil.degToRad(90 - target.direction); + const dx = steps * Math.cos(radians); + const dy = steps * Math.sin(radians); + target.setXY(target.x + dx, target.y + dy); + } + }); + } + `, + + 'sprite2.js': ``, + 'common.js': ``, + }); + const [activeFile, setActiveFile] = useState('game.js'); + + const handleChange = (newCode) => { + // Update the content of the active file + setFiles((prevFiles) => ({ + ...prevFiles, + [activeFile]: newCode, + })); + }; + + const handleTabClick = (fileName) => { + setActiveFile(fileName); + }; + + const insertEventListenerKeys = (targetId, code) => { + return code.replace(`ctx.on(`, `ctx.onWithKey(\"${targetId}\", `); + } + + useEffect(() => { + + console.log(props.vm); + }, []); + + return ( +
+ + {/* File Tabs */} +
+ {Object.keys(files).map((fileName) => ( + + ))} +
+ + {/* Ace Editor */} + +
+ ); +} const messages = defineMessages({ - addExtension: { - id: 'gui.gui.addExtension', - description: 'Button to add an extension in the target pane', - defaultMessage: 'Add Extension' - } + addExtension: { + id: 'gui.gui.addExtension', + description: 'Button to add an extension in the target pane', + defaultMessage: 'Add Extension' + } }); +let webcontainerInstance; + +// await webcontainerInstance.mount(files); + // Cache this value to only retrieve it once the first time. // Assume that it doesn't change for a session. let isRendererSupported = null; const GUIComponent = props => { - const { - accountNavOpen, - activeTabIndex, - alertsVisible, - authorId, - authorThumbnailUrl, - authorUsername, - basePath, - backdropLibraryVisible, - backpackHost, - backpackVisible, - blocksId, - blocksTabVisible, - cardsVisible, - canChangeLanguage, - canChangeTheme, - canCreateNew, - canEditTitle, - canManageFiles, - canRemix, - canSave, - canCreateCopy, - canShare, - canUseCloud, - children, - connectionModalVisible, - costumeLibraryVisible, - costumesTabVisible, - debugModalVisible, - enableCommunity, - intl, - isCreating, - isFullScreen, - isPlayerOnly, - isRtl, - isShared, - isTelemetryEnabled, - isTotallyNormal, - loading, - logo, - renderLogin, - onClickAbout, - onClickAccountNav, - onCloseAccountNav, - onLogOut, - onOpenRegistration, - onToggleLoginOpen, - onActivateCostumesTab, - onActivateSoundsTab, - onActivateTab, - onClickLogo, - onExtensionButtonClick, - onProjectTelemetryEvent, - onRequestCloseBackdropLibrary, - onRequestCloseCostumeLibrary, - onRequestCloseDebugModal, - onRequestCloseTelemetryModal, - onSeeCommunity, - onShare, - onShowPrivacyPolicy, - onStartSelectingFileUpload, - onTelemetryModalCancel, - onTelemetryModalOptIn, - onTelemetryModalOptOut, - showComingSoon, - soundsTabVisible, - stageSizeMode, - targetIsStage, - telemetryModalVisible, - theme, - tipsLibraryVisible, - vm, - ...componentProps - } = omit(props, 'dispatch'); - if (children) { - return {children}; - } - - const tabClassNames = { - tabs: styles.tabs, - tab: classNames(tabStyles.reactTabsTab, styles.tab), - tabList: classNames(tabStyles.reactTabsTabList, styles.tabList), - tabPanel: classNames(tabStyles.reactTabsTabPanel, styles.tabPanel), - tabPanelSelected: classNames(tabStyles.reactTabsTabPanelSelected, styles.isSelected), - tabSelected: classNames(tabStyles.reactTabsTabSelected, styles.isSelected) - }; - - if (isRendererSupported === null) { - isRendererSupported = Renderer.isSupported(); - } - - return ({isFullSize => { - const stageSize = resolveStageSize(stageSizeMode, isFullSize); - - return isPlayerOnly ? ( - - {alertsVisible ? ( - - ) : null} - - ) : ( - - {telemetryModalVisible ? ( - {children}; + } + + const tabClassNames = { + tabs: styles.tabs, + tab: classNames(tabStyles.reactTabsTab, styles.tab), + tabList: classNames(tabStyles.reactTabsTabList, styles.tabList), + tabPanel: classNames(tabStyles.reactTabsTabPanel, styles.tabPanel), + tabPanelSelected: classNames(tabStyles.reactTabsTabPanelSelected, styles.isSelected), + tabSelected: classNames(tabStyles.reactTabsTabSelected, styles.isSelected) + }; + + if (isRendererSupported === null) { + isRendererSupported = Renderer.isSupported(); + } + + + return ({isFullSize => { + const stageSize = resolveStageSize(stageSizeMode, isFullSize); + + return isPlayerOnly ? ( + + {alertsVisible ? ( + + ) : null} + + ) : ( + + {telemetryModalVisible ? ( + + ) : null} + {loading ? ( + + ) : null} + {isCreating ? ( + + ) : null} + {isRendererSupported ? null : ( + + )} + {tipsLibraryVisible ? ( + + ) : null} + {cardsVisible ? ( + + ) : null} + {alertsVisible ? ( + + ) : null} + {connectionModalVisible ? ( + + ) : null} + {costumeLibraryVisible ? ( + + ) : null} + {} + {backdropLibraryVisible ? ( + + ) : null} + + + + {/* + + + + + - ) : null} - {loading ? ( - - ) : null} - {isCreating ? ( - - ) : null} - {isRendererSupported ? null : ( - - )} - {tipsLibraryVisible ? ( - - ) : null} - {cardsVisible ? ( - - ) : null} - {alertsVisible ? ( - - ) : null} - {connectionModalVisible ? ( - + + - ) : null} - {costumeLibraryVisible ? ( - + ) : ( + + )} + + + - ) : null} - {} - {backdropLibraryVisible ? ( - - ) : null} - + + + + + + + + + + + + + + {costumesTabVisible ? : null} + + + {soundsTabVisible ? : null} + + + {backpackVisible ? ( + + ) : null} + */} + + +
+

Editor

+ +
+
+ + + + + - - - - - - - - - - - - {targetIsStage ? ( - - ) : ( - - )} - - - - - - - - - - - - - - - - - - - {costumesTabVisible ? : null} - - - {soundsTabVisible ? : null} - - - {backpackVisible ? ( - - ) : null} - - - - - - - - - - - + - ); - }}
); + + + + + ); + }}
); }; GUIComponent.propTypes = { - accountNavOpen: PropTypes.bool, - activeTabIndex: PropTypes.number, - authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false - authorThumbnailUrl: PropTypes.string, - authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false - backdropLibraryVisible: PropTypes.bool, - backpackHost: PropTypes.string, - backpackVisible: PropTypes.bool, - basePath: PropTypes.string, - blocksTabVisible: PropTypes.bool, - blocksId: PropTypes.string, - canChangeLanguage: PropTypes.bool, - canChangeTheme: PropTypes.bool, - canCreateCopy: PropTypes.bool, - canCreateNew: PropTypes.bool, - canEditTitle: PropTypes.bool, - canManageFiles: PropTypes.bool, - canRemix: PropTypes.bool, - canSave: PropTypes.bool, - canShare: PropTypes.bool, - canUseCloud: PropTypes.bool, - cardsVisible: PropTypes.bool, - children: PropTypes.node, - costumeLibraryVisible: PropTypes.bool, - costumesTabVisible: PropTypes.bool, - debugModalVisible: PropTypes.bool, - enableCommunity: PropTypes.bool, - intl: intlShape.isRequired, - isCreating: PropTypes.bool, - isFullScreen: PropTypes.bool, - isPlayerOnly: PropTypes.bool, - isRtl: PropTypes.bool, - isShared: PropTypes.bool, - isTotallyNormal: PropTypes.bool, - loading: PropTypes.bool, - logo: PropTypes.string, - onActivateCostumesTab: PropTypes.func, - onActivateSoundsTab: PropTypes.func, - onActivateTab: PropTypes.func, - onClickAccountNav: PropTypes.func, - onClickLogo: PropTypes.func, - onCloseAccountNav: PropTypes.func, - onExtensionButtonClick: PropTypes.func, - onLogOut: PropTypes.func, - onOpenRegistration: PropTypes.func, - onRequestCloseBackdropLibrary: PropTypes.func, - onRequestCloseCostumeLibrary: PropTypes.func, - onRequestCloseDebugModal: PropTypes.func, - onRequestCloseTelemetryModal: PropTypes.func, - onSeeCommunity: PropTypes.func, - onShare: PropTypes.func, - onShowPrivacyPolicy: PropTypes.func, - onStartSelectingFileUpload: PropTypes.func, - onTabSelect: PropTypes.func, - onTelemetryModalCancel: PropTypes.func, - onTelemetryModalOptIn: PropTypes.func, - onTelemetryModalOptOut: PropTypes.func, - onToggleLoginOpen: PropTypes.func, - renderLogin: PropTypes.func, - showComingSoon: PropTypes.bool, - soundsTabVisible: PropTypes.bool, - stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)), - targetIsStage: PropTypes.bool, - telemetryModalVisible: PropTypes.bool, - theme: PropTypes.string, - tipsLibraryVisible: PropTypes.bool, - vm: PropTypes.instanceOf(VM).isRequired + accountNavOpen: PropTypes.bool, + activeTabIndex: PropTypes.number, + authorId: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false + authorThumbnailUrl: PropTypes.string, + authorUsername: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]), // can be false + backdropLibraryVisible: PropTypes.bool, + backpackHost: PropTypes.string, + backpackVisible: PropTypes.bool, + basePath: PropTypes.string, + blocksTabVisible: PropTypes.bool, + blocksId: PropTypes.string, + canChangeLanguage: PropTypes.bool, + canChangeTheme: PropTypes.bool, + canCreateCopy: PropTypes.bool, + canCreateNew: PropTypes.bool, + canEditTitle: PropTypes.bool, + canManageFiles: PropTypes.bool, + canRemix: PropTypes.bool, + canSave: PropTypes.bool, + canShare: PropTypes.bool, + canUseCloud: PropTypes.bool, + cardsVisible: PropTypes.bool, + children: PropTypes.node, + costumeLibraryVisible: PropTypes.bool, + costumesTabVisible: PropTypes.bool, + debugModalVisible: PropTypes.bool, + enableCommunity: PropTypes.bool, + intl: intlShape.isRequired, + isCreating: PropTypes.bool, + isFullScreen: PropTypes.bool, + isPlayerOnly: PropTypes.bool, + isRtl: PropTypes.bool, + isShared: PropTypes.bool, + isTotallyNormal: PropTypes.bool, + loading: PropTypes.bool, + logo: PropTypes.string, + onActivateCostumesTab: PropTypes.func, + onActivateSoundsTab: PropTypes.func, + onActivateTab: PropTypes.func, + onClickAccountNav: PropTypes.func, + onClickLogo: PropTypes.func, + onCloseAccountNav: PropTypes.func, + onExtensionButtonClick: PropTypes.func, + onLogOut: PropTypes.func, + onOpenRegistration: PropTypes.func, + onRequestCloseBackdropLibrary: PropTypes.func, + onRequestCloseCostumeLibrary: PropTypes.func, + onRequestCloseDebugModal: PropTypes.func, + onRequestCloseTelemetryModal: PropTypes.func, + onSeeCommunity: PropTypes.func, + onShare: PropTypes.func, + onShowPrivacyPolicy: PropTypes.func, + onStartSelectingFileUpload: PropTypes.func, + onTabSelect: PropTypes.func, + onTelemetryModalCancel: PropTypes.func, + onTelemetryModalOptIn: PropTypes.func, + onTelemetryModalOptOut: PropTypes.func, + onToggleLoginOpen: PropTypes.func, + renderLogin: PropTypes.func, + showComingSoon: PropTypes.bool, + soundsTabVisible: PropTypes.bool, + stageSizeMode: PropTypes.oneOf(Object.keys(STAGE_SIZE_MODES)), + targetIsStage: PropTypes.bool, + telemetryModalVisible: PropTypes.bool, + theme: PropTypes.string, + tipsLibraryVisible: PropTypes.bool, + vm: PropTypes.instanceOf(VM).isRequired }; GUIComponent.defaultProps = { - backpackHost: null, - backpackVisible: false, - basePath: './', - blocksId: 'original', - canChangeLanguage: true, - canChangeTheme: true, - canCreateNew: false, - canEditTitle: false, - canManageFiles: true, - canRemix: false, - canSave: false, - canCreateCopy: false, - canShare: false, - canUseCloud: false, - enableCommunity: false, - isCreating: false, - isShared: false, - isTotallyNormal: false, - loading: false, - showComingSoon: false, - stageSizeMode: STAGE_SIZE_MODES.large + backpackHost: null, + backpackVisible: false, + basePath: './', + blocksId: 'original', + canChangeLanguage: true, + canChangeTheme: true, + canCreateNew: false, + canEditTitle: false, + canManageFiles: true, + canRemix: false, + canSave: false, + canCreateCopy: false, + canShare: false, + canUseCloud: false, + enableCommunity: false, + isCreating: false, + isShared: false, + isTotallyNormal: false, + loading: false, + showComingSoon: false, + stageSizeMode: STAGE_SIZE_MODES.large }; const mapStateToProps = state => ({ - // This is the button's mode, as opposed to the actual current state - blocksId: state.scratchGui.timeTravel.year.toString(), - stageSizeMode: state.scratchGui.stageSize.stageSize, - theme: state.scratchGui.theme.theme + // This is the button's mode, as opposed to the actual current state + blocksId: state.scratchGui.timeTravel.year.toString(), + stageSizeMode: state.scratchGui.stageSize.stageSize, + theme: state.scratchGui.theme.theme }); export default injectIntl(connect( - mapStateToProps + mapStateToProps )(GUIComponent)); diff --git a/src/lib/backpack-api.js b/src/lib/backpack-api.js index 37a0aded036..af2d24065b4 100644 --- a/src/lib/backpack-api.js +++ b/src/lib/backpack-api.js @@ -8,80 +8,82 @@ import codePayload from './backpack/code-payload'; // Also include a full body url for loading sprite zips // TODO retreiving the images through storage would allow us to remove this. const includeFullUrls = (item, host) => Object.assign({}, item, { - thumbnailUrl: `${host}/${item.thumbnail}`, - bodyUrl: `${host}/${item.body}` + thumbnailUrl: `${host}/${item.thumbnail}`, + bodyUrl: `${host}/${item.body}` }); const getBackpackContents = ({ - host, - username, - token, - limit, - offset + host, + username, + token, + limit, + offset }) => new Promise((resolve, reject) => { - xhr({ - method: 'GET', - uri: `${host}/${username}?limit=${limit}&offset=${offset}`, - headers: {'x-token': token}, - json: true - }, (error, response) => { - if (error || response.statusCode !== 200) { - return reject(new Error(response.status)); - } - return resolve(response.body.map(item => includeFullUrls(item, host))); - }); + xhr({ + method: 'GET', + uri: `${host}/${username}?limit=${limit}&offset=${offset}`, + headers: { + 'x-token': token + }, + json: true + }, (error, response) => { + if (error || response.statusCode !== 200) { + return reject(new Error(response.status)); + } + return resolve(response.body.map(item => includeFullUrls(item, host))); + }); }); const saveBackpackObject = ({ - host, - username, - token, - type, // Type of object being saved to the backpack - mime, // Mime-type of the object being saved - name, // User-facing name of the object being saved - body, // Base64-encoded body of the object being saved - thumbnail // Base64-encoded JPEG thumbnail of the object being saved + host, + username, + token, + type, // Type of object being saved to the backpack + mime, // Mime-type of the object being saved + name, // User-facing name of the object being saved + body, // Base64-encoded body of the object being saved + thumbnail // Base64-encoded JPEG thumbnail of the object being saved }) => new Promise((resolve, reject) => { - xhr({ - method: 'POST', - uri: `${host}/${username}`, - headers: {'x-token': token}, - json: {type, mime, name, body, thumbnail} - }, (error, response) => { - if (error || response.statusCode !== 200) { - return reject(new Error(response.status)); - } - return resolve(includeFullUrls(response.body, host)); - }); + xhr({ + method: 'POST', + uri: `${host}/${username}`, + headers: { 'x-token': token }, + json: { type, mime, name, body, thumbnail } + }, (error, response) => { + if (error || response.statusCode !== 200) { + return reject(new Error(response.status)); + } + return resolve(includeFullUrls(response.body, host)); + }); }); const deleteBackpackObject = ({ - host, - username, - token, - id + host, + username, + token, + id }) => new Promise((resolve, reject) => { - xhr({ - method: 'DELETE', - uri: `${host}/${username}/${id}`, - headers: {'x-token': token} - }, (error, response) => { - if (error || response.statusCode !== 200) { - return reject(new Error(response.status)); - } - return resolve(response.body); - }); + xhr({ + method: 'DELETE', + uri: `${host}/${username}/${id}`, + headers: { 'x-token': token } + }, (error, response) => { + if (error || response.statusCode !== 200) { + return reject(new Error(response.status)); + } + return resolve(response.body); + }); }); // Two types of backpack items are not retreivable through storage // code, as json and sprite3 as arraybuffer zips. const fetchAs = (responseType, uri) => new Promise((resolve, reject) => { - xhr({uri, responseType}, (error, response) => { - if (error || response.statusCode !== 200) { - return reject(new Error(response.status)); - } - return resolve(response.body); - }); + xhr({ uri, responseType }, (error, response) => { + if (error || response.statusCode !== 200) { + return reject(new Error(response.status)); + } + return resolve(response.body); + }); }); // These two helpers allow easy fetching of backpack code and sprite zips @@ -90,13 +92,13 @@ const fetchCode = fetchAs.bind(null, 'json'); const fetchSprite = fetchAs.bind(null, 'arraybuffer'); export { - getBackpackContents, - saveBackpackObject, - deleteBackpackObject, - costumePayload, - soundPayload, - spritePayload, - codePayload, - fetchCode, - fetchSprite + getBackpackContents, + saveBackpackObject, + deleteBackpackObject, + costumePayload, + soundPayload, + spritePayload, + codePayload, + fetchCode, + fetchSprite }; diff --git a/src/lib/default-project/index.js b/src/lib/default-project/index.js index d6c80ddd9e1..75fac5d6717 100644 --- a/src/lib/default-project/index.js +++ b/src/lib/default-project/index.js @@ -9,46 +9,77 @@ import costume2 from '!raw-loader!./0fb9be3e8397c983338cb71dc84d0b25.svg?'; /* eslint-enable import/no-unresolved */ const defaultProject = translator => { - let _TextEncoder; - if (typeof TextEncoder === 'undefined') { - _TextEncoder = require('fastestsmallesttextencoderdecoder').TextEncoder; - } else { - _TextEncoder = TextEncoder; - } - const encoder = new _TextEncoder(); + let _TextEncoder; + if (typeof TextEncoder === 'undefined') { + _TextEncoder = require('fastestsmallesttextencoderdecoder').TextEncoder; + } else { + _TextEncoder = TextEncoder; + } + const encoder = new _TextEncoder(); - const projectJson = projectData(translator); - return [{ - id: 0, - assetType: 'Project', - dataFormat: 'JSON', - data: JSON.stringify(projectJson) - }, { - id: '83a9787d4cb6f3b7632b4ddfebf74367', - assetType: 'Sound', - dataFormat: 'WAV', - data: new Uint8Array(popWav) - }, { - id: '83c36d806dc92327b9e7049a565c6bff', - assetType: 'Sound', - dataFormat: 'WAV', - data: new Uint8Array(meowWav) - }, { - id: 'cd21514d0531fdffb22204e0ec5ed84a', - assetType: 'ImageVector', - dataFormat: 'SVG', - data: encoder.encode(backdrop) - }, { - id: 'bcf454acf82e4504149f7ffe07081dbc', - assetType: 'ImageVector', - dataFormat: 'SVG', - data: encoder.encode(costume1) - }, { - id: '0fb9be3e8397c983338cb71dc84d0b25', - assetType: 'ImageVector', - dataFormat: 'SVG', - data: encoder.encode(costume2) - }]; + // const projectJson = { + // "targets": [ + // { + // "isStage": true, + // "name": "Stage", + // "variables": [], + // "lists": [], + // "broadcasts": [], + // "costumes": [], + // "sounds": [], + // "draggable": false, + // "x": 0, + // "y": 0, + // "size": 100, + // "direction": 90, + // "layerOrder": 0, + // "currentCostume": 0, + // "currentSound": 0, + // "volume": 100, + // "tempo": 60, + // "blocks": [], + // "comments": [], + // "threads": [], + // "glow": false + // } + // ] + // }; + + + const projectJson = projectData(translator); + console.log("TESTING"); + console.log(JSON.stringify(projectJson)); + return [{ + id: 0, + assetType: 'Project', + dataFormat: 'JSON', + data: JSON.stringify(projectJson) + }, { + id: '83a9787d4cb6f3b7632b4ddfebf74367', + assetType: 'Sound', + dataFormat: 'WAV', + data: new Uint8Array(popWav) + }, { + id: '83c36d806dc92327b9e7049a565c6bff', + assetType: 'Sound', + dataFormat: 'WAV', + data: new Uint8Array(meowWav) + }, { + id: 'cd21514d0531fdffb22204e0ec5ed84a', + assetType: 'ImageVector', + dataFormat: 'SVG', + data: encoder.encode(backdrop) + }, { + id: 'bcf454acf82e4504149f7ffe07081dbc', + assetType: 'ImageVector', + dataFormat: 'SVG', + data: encoder.encode(costume1) + }, { + id: '0fb9be3e8397c983338cb71dc84d0b25', + assetType: 'ImageVector', + dataFormat: 'SVG', + data: encoder.encode(costume2) + }]; }; export default defaultProject; diff --git a/src/lib/default-project/project-data.js b/src/lib/default-project/project-data.js index 9ee63b237b5..640919465f7 100644 --- a/src/lib/default-project/project-data.js +++ b/src/lib/default-project/project-data.js @@ -1,20 +1,20 @@ -import {defineMessages} from 'react-intl'; +import { defineMessages } from 'react-intl'; import sharedMessages from '../shared-messages'; let messages = defineMessages({ - meow: { - defaultMessage: 'Meow', - description: 'Name for the meow sound', - id: 'gui.defaultProject.meow' - }, - variable: { - defaultMessage: 'my variable', - description: 'Name for the default variable', - id: 'gui.defaultProject.variable' - } + meow: { + defaultMessage: 'Meow', + description: 'Name for the meow sound', + id: 'gui.defaultProject.meow' + }, + variable: { + defaultMessage: 'my variable', + description: 'Name for the default variable', + id: 'gui.defaultProject.variable' + } }); -messages = {...messages, ...sharedMessages}; +messages = { ...messages, ...sharedMessages }; // use the default message if a translation function is not passed const defaultTranslator = msgObj => msgObj.defaultMessage; @@ -25,100 +25,174 @@ const defaultTranslator = msgObj => msgObj.defaultMessage; * @return {object} the project data json for the default project */ const projectData = translateFunction => { - const translator = translateFunction || defaultTranslator; - return ({ - targets: [ - { - isStage: true, - name: 'Stage', - variables: { - '`jEk@4|i[#Fk?(8x)AV.-my variable': [ - translator(messages.variable), - 0 - ] - }, - lists: {}, - broadcasts: {}, - blocks: {}, - currentCostume: 0, - costumes: [ - { - assetId: 'cd21514d0531fdffb22204e0ec5ed84a', - name: translator(messages.backdrop, {index: 1}), - md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg', - dataFormat: 'svg', - rotationCenterX: 240, - rotationCenterY: 180 - } - ], - sounds: [ - { - assetId: '83a9787d4cb6f3b7632b4ddfebf74367', - name: translator(messages.pop), - dataFormat: 'wav', - format: '', - rate: 11025, - sampleCount: 258, - md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav' - } - ], - volume: 100 - }, - { - isStage: false, - name: translator(messages.sprite, {index: 1}), - variables: {}, - lists: {}, - broadcasts: {}, - blocks: {}, - currentCostume: 0, - costumes: [ - { - assetId: 'bcf454acf82e4504149f7ffe07081dbc', - name: translator(messages.costume, {index: 1}), - bitmapResolution: 1, - md5ext: 'bcf454acf82e4504149f7ffe07081dbc.svg', - dataFormat: 'svg', - rotationCenterX: 48, - rotationCenterY: 50 - }, - { - assetId: '0fb9be3e8397c983338cb71dc84d0b25', - name: translator(messages.costume, {index: 2}), - bitmapResolution: 1, - md5ext: '0fb9be3e8397c983338cb71dc84d0b25.svg', - dataFormat: 'svg', - rotationCenterX: 46, - rotationCenterY: 53 - } - ], - sounds: [ - { - assetId: '83c36d806dc92327b9e7049a565c6bff', - name: translator(messages.meow), - dataFormat: 'wav', - format: '', - rate: 22050, - sampleCount: 18688, - md5ext: '83c36d806dc92327b9e7049a565c6bff.wav' - } - ], - volume: 100, - visible: true, - x: 0, - y: 0, - size: 100, - direction: 90, - draggable: false, - rotationStyle: 'all around' - } + const translator = translateFunction || defaultTranslator; + return ({ + targets: [ + { + isStage: true, + name: 'Stage', + variables: { + '`jEk@4|i[#Fk?(8x)AV.-my variable': [ + translator(messages.variable), + 0 + ] + }, + lists: {}, + broadcasts: {}, + blocks: { + + }, + currentCostume: 0, + costumes: [ + { + assetId: 'cd21514d0531fdffb22204e0ec5ed84a', + name: translator(messages.backdrop, { index: 1 }), + md5ext: 'cd21514d0531fdffb22204e0ec5ed84a.svg', + dataFormat: 'svg', + rotationCenterX: 240, + rotationCenterY: 180 + } + ], + sounds: [ + { + assetId: '83a9787d4cb6f3b7632b4ddfebf74367', + name: translator(messages.pop), + dataFormat: 'wav', + format: '', + rate: 11025, + sampleCount: 258, + md5ext: '83a9787d4cb6f3b7632b4ddfebf74367.wav' + } ], - meta: { - semver: '3.0.0', - vm: '0.1.0', - agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' // eslint-disable-line max-len - } - }); + volume: 100 + }, + { + isStage: false, + name: translator(messages.sprite, { index: 1 }), + variables: {}, + lists: {}, + broadcasts: {}, + blocks: {}, + + // "0uLn5uNtj`ki1_5z+(Ba": { + // "opcode": "motion_movesteps", + // "next": null, + // "parent": "$)k)Sps+=$QVkZ?%mYJz", + // "inputs": { + // "STEPS": [ + // 1, + // [ + // 4, + // "-10" + // ] + // ] + // }, + // "fields": {}, + // "shadow": false, + // "topLevel": false + // }, + // "$)k)Sps+=$QVkZ?%mYJz": { + // "opcode": "event_whenkeypressed", + // "next": "0uLn5uNtj`ki1_5z+(Ba", + // "parent": null, + // "inputs": {}, + // "fields": { + // "KEY_OPTION": [ + // "left arrow", + // null + // ] + // }, + // "shadow": false, + // "topLevel": true, + // "x": 89, + // "y": 489 + // }, + // "@-EZS$Z6997_]0sE~9ye": { + // "opcode": "event_whenkeypressed", + // "next": "Pd)LrM7N=8c~p^hrMXtb", + // "parent": null, + // "inputs": {}, + // "fields": { + // "KEY_OPTION": [ + // "right arrow", + // null + // ] + // }, + // "shadow": false, + // "topLevel": true, + // "x": 126, + // "y": 308 + // }, + // "Pd)LrM7N=8c~p^hrMXtb": { + // "opcode": "motion_movesteps", + // "next": null, + // "parent": "@-EZS$Z6997_]0sE~9ye", + // "inputs": { + // "STEPS": [ + // 1, + // [ + // 4, + // "10" + // ] + // ] + // }, + // "fields": {}, + // "shadow": false, + // "topLevel": false + // } + + currentCostume: 0, + costumes: [ + { + assetId: 'bcf454acf82e4504149f7ffe07081dbc', + name: translator(messages.costume, { index: 1 }), + bitmapResolution: 1, + md5ext: 'bcf454acf82e4504149f7ffe07081dbc.svg', + dataFormat: 'svg', + rotationCenterX: 48, + rotationCenterY: 50 + }, + { + assetId: '0fb9be3e8397c983338cb71dc84d0b25', + name: translator(messages.costume, { index: 2 }), + bitmapResolution: 1, + md5ext: '0fb9be3e8397c983338cb71dc84d0b25.svg', + dataFormat: 'svg', + rotationCenterX: 46, + rotationCenterY: 53 + } + ], + scripts: [ + "0uLn5uNtj`ki1_5z+(Ba", + "$)k)Sps+=$QVkZ?%mYJz" + ], + sounds: [ + { + assetId: '83c36d806dc92327b9e7049a565c6bff', + name: translator(messages.meow), + dataFormat: 'wav', + format: '', + rate: 22050, + sampleCount: 18688, + md5ext: '83c36d806dc92327b9e7049a565c6bff.wav' + } + ], + volume: 100, + visible: true, + x: 0, + y: 0, + size: 100, + direction: 90, + draggable: false, + rotationStyle: 'all around' + } + ], + meta: { + semver: '3.0.0', + vm: '0.1.0', + agent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36' // eslint-disable-line max-len + } + }); }; diff --git a/src/playground/index.ejs b/src/playground/index.ejs index abbd5d8b024..9846e37ccbb 100644 --- a/src/playground/index.ejs +++ b/src/playground/index.ejs @@ -1,27 +1,43 @@ - - <% if (htmlWebpackPlugin.options.gtm_id) { %> + + + <% if (htmlWebpackPlugin.options.gtm_id) { %> - <% } %> - - - - - <%= htmlWebpackPlugin.options.title %> - - - <% if (htmlWebpackPlugin.options.gtm_id) { %> - - - + + + + + + <%= htmlWebpackPlugin.options.title %> + + + + + + + + + + <% if (htmlWebpackPlugin.options.gtm_id) { %> + + + <% } %> - + + diff --git a/webpack.config.js b/webpack.config.js index 0ec57fa85e7..4e31aa172bd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -10,157 +10,164 @@ const ScratchWebpackConfigBuilder = require('scratch-webpack-configuration'); // const STATIC_PATH = process.env.STATIC_PATH || '/static'; const commonHtmlWebpackPluginOptions = { - // Google Tag Manager ID - // Looks like 'GTM-XXXXXXX' - gtm_id: process.env.GTM_ID || '', + // Google Tag Manager ID + // Looks like 'GTM-XXXXXXX' + gtm_id: process.env.GTM_ID || '', - // Google Tag Manager env & auth info for alterative GTM environments - // Looks like '>m_auth=0123456789abcdefghijklm>m_preview=env-00>m_cookies_win=x' - // Taken from the middle of: GTM -> Admin -> Environments -> (environment) -> Get Snippet - // Blank for production - gtm_env_auth: process.env.GTM_ENV_AUTH || '' + // Google Tag Manager env & auth info for alterative GTM environments + // Looks like '>m_auth=0123456789abcdefghijklm>m_preview=env-00>m_cookies_win=x' + // Taken from the middle of: GTM -> Admin -> Environments -> (environment) -> Get Snippet + // Blank for production + gtm_env_auth: process.env.GTM_ENV_AUTH || '' }; const baseConfig = new ScratchWebpackConfigBuilder( - { - rootPath: path.resolve(__dirname), - enableReact: true, - shouldSplitChunks: false, - publicPath: 'auto' - }) - .setTarget('browserslist') - .merge({ - output: { - assetModuleFilename: 'static/assets/[name].[hash][ext][query]', - library: { - name: 'GUI', - type: 'umd2' - } - }, - resolve: { - fallback: { - Buffer: require.resolve('buffer/'), - stream: require.resolve('stream-browserify') - } - } - }) - .addModuleRule({ - test: /\.(svg|png|wav|mp3|gif|jpg)$/, - resourceQuery: /^$/, // reject any query string - type: 'asset' // let webpack decide on the best type of asset - }) - .addPlugin(new webpack.DefinePlugin({ - 'process.env.DEBUG': Boolean(process.env.DEBUG), - 'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`, - 'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`, - 'process.env.GTM_ID': process.env.GTM_ID ? `"${process.env.GTM_ID}"` : null - })) - .addPlugin(new CopyWebpackPlugin({ - patterns: [ - { - from: 'node_modules/scratch-blocks/media', - to: 'static/blocks-media/default' - }, - { - from: 'node_modules/scratch-blocks/media', - to: 'static/blocks-media/high-contrast' - }, - { - // overwrite some of the default block media with high-contrast versions - // this entry must come after copying scratch-blocks/media into the high-contrast directory - from: 'src/lib/themes/high-contrast/blocks-media', - to: 'static/blocks-media/high-contrast', - force: true - }, - { - context: 'node_modules/scratch-vm/dist/web', - from: 'extension-worker.{js,js.map}', - noErrorOnMissing: true - } - ] - })); + { + rootPath: path.resolve(__dirname), + enableReact: true, + shouldSplitChunks: false, + publicPath: 'auto' + }) + .setTarget('browserslist') + .merge({ + output: { + assetModuleFilename: 'static/assets/[name].[hash][ext][query]', + library: { + name: 'GUI', + type: 'umd2' + } + }, + resolve: { + fallback: { + Buffer: require.resolve('buffer/'), + stream: require.resolve('stream-browserify') + } + } + }) + .addModuleRule({ + test: /\.(svg|png|wav|mp3|gif|jpg)$/, + resourceQuery: /^$/, // reject any query string + type: 'asset' // let webpack decide on the best type of asset + }) + .addPlugin(new webpack.DefinePlugin({ + 'process.env.DEBUG': Boolean(process.env.DEBUG), + 'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`, + 'process.env.GTM_ENV_AUTH': `"${process.env.GTM_ENV_AUTH || ''}"`, + 'process.env.GTM_ID': process.env.GTM_ID ? `"${process.env.GTM_ID}"` : null + })) + .addPlugin(new CopyWebpackPlugin({ + patterns: [ + { + from: 'node_modules/scratch-blocks/media', + to: 'static/blocks-media/default' + }, + { + from: 'node_modules/scratch-blocks/media', + to: 'static/blocks-media/high-contrast' + }, + { + // overwrite some of the default block media with high-contrast versions + // this entry must come after copying scratch-blocks/media into the high-contrast directory + from: 'src/lib/themes/high-contrast/blocks-media', + to: 'static/blocks-media/high-contrast', + force: true + }, + { + context: 'node_modules/scratch-vm/dist/web', + from: 'extension-worker.{js,js.map}', + noErrorOnMissing: true + } + ] + })); if (!process.env.CI) { - baseConfig.addPlugin(new webpack.ProgressPlugin()); + baseConfig.addPlugin(new webpack.ProgressPlugin()); } // build the shipping library in `dist/` const distConfig = baseConfig.clone() - .merge({ - entry: { - 'scratch-gui': path.join(__dirname, 'src/index.js') - }, - output: { - path: path.resolve(__dirname, 'dist') + .merge({ + entry: { + 'scratch-gui': path.join(__dirname, 'src/index.js') + }, + output: { + path: path.resolve(__dirname, 'dist') + } + }) + .addExternals(['react', 'react-dom']) + .addPlugin( + new CopyWebpackPlugin({ + patterns: [ + { + from: 'src/lib/libraries/*.json', + to: 'libraries', + flatten: true } + ] }) - .addExternals(['react', 'react-dom']) - .addPlugin( - new CopyWebpackPlugin({ - patterns: [ - { - from: 'src/lib/libraries/*.json', - to: 'libraries', - flatten: true - } - ] - }) - ); + ); // build the examples and debugging tools in `build/` const buildConfig = baseConfig.clone() - .enableDevServer(process.env.PORT || 8601) - .merge({ - entry: { - gui: './src/playground/index.jsx', - blocksonly: './src/playground/blocks-only.jsx', - compatibilitytesting: './src/playground/compatibility-testing.jsx', - player: './src/playground/player.jsx' - }, - output: { - path: path.resolve(__dirname, 'build') - } - }) - .addPlugin(new HtmlWebpackPlugin({ - ...commonHtmlWebpackPluginOptions, - chunks: ['gui'], - template: 'src/playground/index.ejs', - title: 'Scratch 3.0 GUI' - })) - .addPlugin(new HtmlWebpackPlugin({ - ...commonHtmlWebpackPluginOptions, - chunks: ['blocksonly'], - filename: 'blocks-only.html', - template: 'src/playground/index.ejs', - title: 'Scratch 3.0 GUI: Blocks Only Example' - })) - .addPlugin(new HtmlWebpackPlugin({ - ...commonHtmlWebpackPluginOptions, - chunks: ['compatibilitytesting'], - filename: 'compatibility-testing.html', - template: 'src/playground/index.ejs', - title: 'Scratch 3.0 GUI: Compatibility Testing' - })) - .addPlugin(new HtmlWebpackPlugin({ - ...commonHtmlWebpackPluginOptions, - chunks: ['player'], - filename: 'player.html', - template: 'src/playground/index.ejs', - title: 'Scratch 3.0 GUI: Player Example' - })) - .addPlugin(new CopyWebpackPlugin({ - patterns: [ - { - from: 'static', - to: 'static' - }, - { - from: 'extensions/**', - to: 'static', - context: 'src/examples' - } - ] - })); + .enableDevServer(process.env.PORT || 8601, { + headers: { + 'Cross-Origin-Embedder-Policy': 'require-corp', + 'Cross-Origin-Opener-Policy': 'same-origin', + 'Cross-Origin-Resource-Policy': 'cross-origin', + 'Access-Control-Allow-Origin': '*' // Ensure proper CORS handling + } + }) + .merge({ + entry: { + gui: './src/playground/index.jsx', + blocksonly: './src/playground/blocks-only.jsx', + compatibilitytesting: './src/playground/compatibility-testing.jsx', + player: './src/playground/player.jsx' + }, + output: { + path: path.resolve(__dirname, 'build') + } + }) + .addPlugin(new HtmlWebpackPlugin({ + ...commonHtmlWebpackPluginOptions, + chunks: ['gui'], + template: 'src/playground/index.ejs', + title: 'Scratch 3.0 GUI' + })) + .addPlugin(new HtmlWebpackPlugin({ + ...commonHtmlWebpackPluginOptions, + chunks: ['blocksonly'], + filename: 'blocks-only.html', + template: 'src/playground/index.ejs', + title: 'Scratch 3.0 GUI: Blocks Only Example' + })) + .addPlugin(new HtmlWebpackPlugin({ + ...commonHtmlWebpackPluginOptions, + chunks: ['compatibilitytesting'], + filename: 'compatibility-testing.html', + template: 'src/playground/index.ejs', + title: 'Scratch 3.0 GUI: Compatibility Testing' + })) + .addPlugin(new HtmlWebpackPlugin({ + ...commonHtmlWebpackPluginOptions, + chunks: ['player'], + filename: 'player.html', + template: 'src/playground/index.ejs', + title: 'Scratch 3.0 GUI: Player Example' + })) + .addPlugin(new CopyWebpackPlugin({ + patterns: [ + { + from: 'static', + to: 'static' + }, + { + from: 'extensions/**', + to: 'static', + context: 'src/examples' + } + ] + })); // Skip building `dist/` unless explicitly requested // It roughly doubles build time and isn't needed for `scratch-gui` development @@ -169,5 +176,5 @@ const buildConfig = baseConfig.clone() const buildDist = process.env.NODE_ENV === 'production' || process.env.BUILD_MODE === 'dist'; module.exports = buildDist ? - [buildConfig.get(), distConfig.get()] : - buildConfig.get(); + [buildConfig.get(), distConfig.get()] : + buildConfig.get();