├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── scratch-gui └── src │ └── lib │ └── libraries │ └── extensions │ ├── controlplus │ ├── controlplus-button-illustration.svg │ ├── controlplus-illustration.svg │ ├── controlplus-small.svg │ ├── controlplus.png │ └── index.jsx │ ├── duplotrain │ ├── duplotrain-button-illustration.svg │ ├── duplotrain-illustration.svg │ ├── duplotrain-small.svg │ ├── duplotrain.png │ └── index.jsx │ ├── legoble │ ├── index.jsx │ ├── legoble-illustration.svg │ ├── legoble-small.svg │ └── legoble.png │ ├── legoluigi │ ├── index.jsx │ ├── legoluigi-button-illustration.svg │ ├── legoluigi-illustration.svg │ ├── legoluigi-small.svg │ └── legoluigi.png │ ├── legomario │ ├── index.jsx │ ├── legomario-button-illustration.svg │ ├── legomario-illustration.svg │ ├── legomario-small.svg │ └── legomario.png │ ├── legopeach │ ├── index.jsx │ ├── legopeach-button-illustration.svg │ ├── legopeach-illustration.svg │ ├── legopeach-small.svg │ └── legopeach.png │ ├── legoremote │ ├── index.jsx │ ├── legoremote-button-illustration.svg │ ├── legoremote-illustration.svg │ ├── legoremote-small.svg │ └── legoremote.png │ ├── poweredup │ ├── index.jsx │ ├── poweredup-button-illustration.svg │ ├── poweredup-illustration.svg │ ├── poweredup-small.svg │ └── poweredup.png │ ├── spikeessential │ ├── index.jsx │ ├── spikeessential-button-illustration.svg │ ├── spikeessential-illustration.svg │ ├── spikeessential-small.svg │ └── spikeessential.png │ └── spikeprime │ ├── index.jsx │ ├── spikeprime-illustration.svg │ ├── spikeprime-small.svg │ └── spikeprime.png ├── scratch-vm └── src │ └── extensions │ ├── scratch3_controlplus │ ├── index.js │ └── lib │ ├── scratch3_duplotrain │ ├── index.js │ └── lib │ ├── scratch3_legoble │ ├── index.js │ └── lib │ │ ├── ble-base-blocks.js │ │ ├── color.js │ │ ├── device.js │ │ ├── hub.js │ │ ├── io-type.js │ │ ├── mario-base-blocks.js │ │ └── setup-translations.js │ ├── scratch3_legoluigi │ ├── index.js │ └── lib │ ├── scratch3_legomario │ ├── index.js │ └── lib │ ├── scratch3_legopeach │ ├── index.js │ └── lib │ ├── scratch3_legoremote │ ├── index.js │ └── lib │ ├── scratch3_poweredup │ ├── index.js │ └── lib │ ├── scratch3_spikeessential │ ├── index.js │ └── lib │ └── scratch3_spikeprime │ ├── index.js │ └── lib └── scripts ├── install.sh └── register.js /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags: 7 | - "!*" 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build-deploy: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Install Node.js 14 15 | uses: actions/setup-node@v3 16 | with: 17 | node-version: 14 18 | 19 | - name: Checkout bricklife/scratch-gui 20 | uses: actions/checkout@v2 21 | with: 22 | repository: bricklife/scratch-gui 23 | ref: lego-bluetooth-extensions 24 | path: ./scratch-gui 25 | 26 | - name: Run `npm ci` in scratch-gui 27 | run: npm ci 28 | working-directory: ./scratch-gui 29 | 30 | - name: Checkout bricklife/scratch-lego-bluetooth-extensions 31 | uses: actions/checkout@v2 32 | with: 33 | repository: bricklife/scratch-lego-bluetooth-extensions 34 | ref: master 35 | path: ./scratch-lego-bluetooth-extensions 36 | 37 | - name: Run `npm ci` in scratch-lego-bluetooth-extensions 38 | run: npm ci 39 | working-directory: ./scratch-lego-bluetooth-extensions 40 | 41 | - name: Register all extensions 42 | run: npm run register 43 | working-directory: ./scratch-lego-bluetooth-extensions 44 | 45 | - name: Build scratch-gui 46 | run: npm run build 47 | working-directory: ./scratch-gui 48 | 49 | - name: Build all mjs files 50 | run: npm run build 51 | working-directory: ./scratch-lego-bluetooth-extensions 52 | 53 | - name: Copy all mjs files 54 | run: cp -r ./scratch-lego-bluetooth-extensions/dist ./scratch-gui/build/xcratch 55 | 56 | - name: Deploy 57 | uses: peaceiris/actions-gh-pages@v3 58 | with: 59 | github_token: ${{ secrets.GITHUB_TOKEN }} 60 | publish_dir: ./scratch-gui/build 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | build 4 | dist 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Shinichiro Oba 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # scratch-lego-bluetooth-extensions 2 | Scratch 3.0 extensions for LEGO Bluetooth devices. 3 | 4 | ## Extension List 5 | Bluetooth LE (BLE) 6 | - [x] LEGO Powered UP Hub 7 | - [x] LEGO Powered UP Remote Control 8 | - [x] LEGO Technic CONTROL+ Hub 9 | - [x] LEGO DUPLO Train 10 | - [x] LEGO Mario 11 | - [x] LEGO Luigi 12 | - [x] LEGO Peach 13 | - [x] LEGO Education SPIKE Essential Hub 14 | - [x] General LEGO BLE Device 15 | 16 | Bluetooth Classic (SPP) 17 | - [x] ~~LEGO Education SPIKE Prime Hub (Legacy) <- Not working on Windows~~ 18 | - [ ] LEGO MINDSTORMS Robot Inventor Hub 19 | 20 | ## Scratch 3.0 installed LEGO Bluetooth extensions 21 | - https://bricklife.com/scratch-gui/ (Stable version) 22 | - https://bricklife.com/scratch-lego-bluetooth-extensions/ (Development version) 23 | 24 | ## Xcratch 25 | [Xcratch](https://xcratch.github.io) is an extendable Scratch programming environment. You can load LEGO Bluetooth extensions into Xcratch from the following URLs: 26 | - LEGO Powered UP Hub https://bricklife.com/scratch-gui/xcratch/poweredup.mjs 27 | - LEGO Powered UP Remote Control https://bricklife.com/scratch-gui/xcratch/legoremote.mjs 28 | - LEGO Technic CONTROL+ Hub https://bricklife.com/scratch-gui/xcratch/controlplus.mjs 29 | - LEGO DUPLO Train https://bricklife.com/scratch-gui/xcratch/duplotrain.mjs 30 | - LEGO Mario https://bricklife.com/scratch-gui/xcratch/legomario.mjs 31 | - LEGO Luigi https://bricklife.com/scratch-gui/xcratch/legoluigi.mjs 32 | - LEGO Peach https://bricklife.com/scratch-gui/xcratch/legopeach.mjs 33 | - LEGO Education SPIKE Essential Hub https://bricklife.com/scratch-gui/xcratch/spikeessential.mjs 34 | - General LEGO BLE Device https://bricklife.com/scratch-gui/xcratch/legoble.mjs 35 | - ~~LEGO Education SPIKE Prime Hub (Legacy) https://bricklife.com/scratch-gui/xcratch/spikeprime.mjs~~ 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scratch-lego-bluetooth-extensions", 3 | "version": "0.1.0", 4 | "description": "Scratch 3.0 extensions for LEGO Bluetooth and BLE devices", 5 | "scripts": { 6 | "register": "npm run register:legoble && npm run register:spikeessential && npm run register:legoremote && npm run register:controlplus && npm run register:poweredup && npm run register:duplotrain && npm run register:legopeach && npm run register:legoluigi && npm run register:legomario", 7 | "register:spikeprime": "node ./scripts/register.js --link -C --id=\"spikeprime\" --block=\"./scratch-vm/src/extensions/scratch3_spikeprime\" --entry=\"./scratch-gui/src/lib/libraries/extensions/spikeprime\" --gui=\"../scratch-gui\"", 8 | "register:legoble": "node ./scripts/register.js --link -C --id=\"legoble\" --block=\"./scratch-vm/src/extensions/scratch3_legoble\" --entry=\"./scratch-gui/src/lib/libraries/extensions/legoble\" --gui=\"../scratch-gui\"", 9 | "register:spikeessential": "node ./scripts/register.js --link -C --id=\"spikeessential\" --block=\"./scratch-vm/src/extensions/scratch3_spikeessential\" --entry=\"./scratch-gui/src/lib/libraries/extensions/spikeessential\" --gui=\"../scratch-gui\"", 10 | "register:legoremote": "node ./scripts/register.js --link -C --id=\"legoremote\" --block=\"./scratch-vm/src/extensions/scratch3_legoremote\" --entry=\"./scratch-gui/src/lib/libraries/extensions/legoremote\" --gui=\"../scratch-gui\"", 11 | "register:controlplus": "node ./scripts/register.js --link -C --id=\"controlplus\" --block=\"./scratch-vm/src/extensions/scratch3_controlplus\" --entry=\"./scratch-gui/src/lib/libraries/extensions/controlplus\" --gui=\"../scratch-gui\"", 12 | "register:poweredup": "node ./scripts/register.js --link -C --id=\"poweredup\" --block=\"./scratch-vm/src/extensions/scratch3_poweredup\" --entry=\"./scratch-gui/src/lib/libraries/extensions/poweredup\" --gui=\"../scratch-gui\"", 13 | "register:duplotrain": "node ./scripts/register.js --link -C --id=\"duplotrain\" --block=\"./scratch-vm/src/extensions/scratch3_duplotrain\" --entry=\"./scratch-gui/src/lib/libraries/extensions/duplotrain\" --gui=\"../scratch-gui\"", 14 | "register:legomario": "node ./scripts/register.js --link -C --id=\"legomario\" --block=\"./scratch-vm/src/extensions/scratch3_legomario\" --entry=\"./scratch-gui/src/lib/libraries/extensions/legomario\" --gui=\"../scratch-gui\"", 15 | "register:legoluigi": "node ./scripts/register.js --link -C --id=\"legoluigi\" --block=\"./scratch-vm/src/extensions/scratch3_legoluigi\" --entry=\"./scratch-gui/src/lib/libraries/extensions/legoluigi\" --gui=\"../scratch-gui\"", 16 | "register:legopeach": "node ./scripts/register.js --link -C --id=\"legopeach\" --block=\"./scratch-vm/src/extensions/scratch3_legopeach\" --entry=\"./scratch-gui/src/lib/libraries/extensions/legopeach\" --gui=\"../scratch-gui\"", 17 | "build": "npm run build:legoble && npm run build:spikeessential && npm run build:legoremote && npm run build:controlplus && npm run build:poweredup && npm run build:duplotrain && npm run build:legopeach && npm run build:legoluigi && npm run build:legomario", 18 | "build:spikeprime": "npx xcratch-build --module=\"spikeprime\" --block=\"scratch-vm/src/extensions/scratch3_spikeprime\" --entry=\"scratch-gui/src/lib/libraries/extensions/spikeprime\" --gui=\"../scratch-gui\"", 19 | "build:legoble": "npx xcratch-build --module=\"legoble\" --block=\"scratch-vm/src/extensions/scratch3_legoble\" --entry=\"scratch-gui/src/lib/libraries/extensions/legoble\" --gui=\"../scratch-gui\"", 20 | "build:spikeessential": "npx xcratch-build --module=\"spikeessential\" --block=\"scratch-vm/src/extensions/scratch3_spikeessential\" --entry=\"scratch-gui/src/lib/libraries/extensions/spikeessential\" --gui=\"../scratch-gui\"", 21 | "build:legoremote": "npx xcratch-build --module=\"legoremote\" --block=\"scratch-vm/src/extensions/scratch3_legoremote\" --entry=\"scratch-gui/src/lib/libraries/extensions/legoremote\" --gui=\"../scratch-gui\"", 22 | "build:controlplus": "npx xcratch-build --module=\"controlplus\" --block=\"scratch-vm/src/extensions/scratch3_controlplus\" --entry=\"scratch-gui/src/lib/libraries/extensions/controlplus\" --gui=\"../scratch-gui\"", 23 | "build:poweredup": "npx xcratch-build --module=\"poweredup\" --block=\"scratch-vm/src/extensions/scratch3_poweredup\" --entry=\"scratch-gui/src/lib/libraries/extensions/poweredup\" --gui=\"../scratch-gui\"", 24 | "build:duplotrain": "npx xcratch-build --module=\"duplotrain\" --block=\"scratch-vm/src/extensions/scratch3_duplotrain\" --entry=\"scratch-gui/src/lib/libraries/extensions/duplotrain\" --gui=\"../scratch-gui\"", 25 | "build:legomario": "npx xcratch-build --module=\"legomario\" --block=\"scratch-vm/src/extensions/scratch3_legomario\" --entry=\"scratch-gui/src/lib/libraries/extensions/legomario\" --gui=\"../scratch-gui\"", 26 | "build:legoluigi": "npx xcratch-build --module=\"legoluigi\" --block=\"scratch-vm/src/extensions/scratch3_legoluigi\" --entry=\"scratch-gui/src/lib/libraries/extensions/legoluigi\" --gui=\"../scratch-gui\"", 27 | "build:legopeach": "npx xcratch-build --module=\"legopeach\" --block=\"scratch-vm/src/extensions/scratch3_legopeach\" --entry=\"scratch-gui/src/lib/libraries/extensions/legopeach\" --gui=\"../scratch-gui\"", 28 | "start": "cd ../scratch-gui && npm run start" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/bricklife/scratch-lego-bluetooth-extensions.git" 33 | }, 34 | "keywords": [ 35 | "scratch", 36 | "lego", 37 | "bluetooth", 38 | "ble" 39 | ], 40 | "author": "Shinichiro Oba", 41 | "license": "MIT", 42 | "bugs": { 43 | "url": "https://github.com/bricklife/scratch-lego-bluetooth-extensions/issues" 44 | }, 45 | "homepage": "https://github.com/bricklife/scratch-lego-bluetooth-extensions", 46 | "devDependencies": { 47 | "xcratch-build": "github:bricklife/xcratch-build#lego-bluetooth-extensions" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/controlplus/controlplus-illustration.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/controlplus/controlplus-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | controlplus-small 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/controlplus/controlplus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/controlplus/controlplus.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/controlplus/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import controlplusIconURL from './controlplus.png'; 5 | import controlplusInsetIconURL from './controlplus-small.svg'; 6 | import controlplusConnectionIconURL from './controlplus-illustration.svg'; 7 | import controlplusConnectionSmallIconURL from './controlplus-small.svg'; 8 | import controlplusConnectionTipIconURL from './controlplus-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Technic CONTROL+', 12 | extensionId: 'controlplus', 13 | collaborator: 'bricklife', 14 | iconURL: controlplusIconURL, 15 | insetIconURL: controlplusInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: controlplusConnectionIconURL, 29 | connectionSmallIconURL: controlplusConnectionSmallIconURL, 30 | connectionTipIconURL: controlplusConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain-button-illustration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | duplotrain-button-illustration 21 | Created with Sketch. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain-illustration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | duplotrain-illustration 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | duplotrain-small 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/duplotrain/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import duplotrainIconURL from './duplotrain.png'; 5 | import duplotrainInsetIconURL from './duplotrain-small.svg'; 6 | import duplotrainConnectionIconURL from './duplotrain-illustration.svg'; 7 | import duplotrainConnectionSmallIconURL from './duplotrain-small.svg'; 8 | import duplotrainConnectionTipIconURL from './duplotrain-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO DUPLO Train', 12 | extensionId: 'duplotrain', 13 | collaborator: 'bricklife', 14 | iconURL: duplotrainIconURL, 15 | insetIconURL: duplotrainInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: duplotrainConnectionIconURL, 29 | connectionSmallIconURL: duplotrainConnectionSmallIconURL, 30 | connectionTipIconURL: duplotrainConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost', 38 | translationMap: { 39 | 'en': { 40 | 'gui.extension.duplotrain.description': 'Build and control your train.' 41 | }, 42 | 'ja': { 43 | 'gui.extension.duplotrain.description': 'レゴ デュプロの機関車を動かそう。' 44 | }, 45 | 'ja-Hira': { 46 | 'gui.extension.duplotrain.description': 'レゴ デュプロのできかんしゃをうごかそう。' 47 | } 48 | } 49 | }; 50 | 51 | export {entry}; // loadable-extension needs this line. 52 | export default entry; 53 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoble/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import legobleIconURL from './legoble.png'; 5 | import legobleInsetIconURL from './legoble-small.svg'; 6 | import legobleConnectionIconURL from './legoble-illustration.svg'; 7 | import legobleConnectionSmallIconURL from './legoble-small.svg'; 8 | 9 | const entry = { 10 | name: 'LEGO BLE Device', 11 | extensionId: 'legoble', 12 | collaborator: 'bricklife', 13 | iconURL: legobleIconURL, 14 | insetIconURL: legobleInsetIconURL, 15 | description: ( 16 | 20 | ), 21 | featured: true, 22 | disabled: false, 23 | bluetoothRequired: true, 24 | internetConnectionRequired: true, 25 | launchPeripheralConnectionFlow: true, 26 | useAutoScan: false, 27 | connectionIconURL: legobleConnectionIconURL, 28 | connectionSmallIconURL: legobleConnectionSmallIconURL, 29 | connectingMessage: ( 30 | 34 | ), 35 | helpLink: 'https://scratch.mit.edu/boost' 36 | }; 37 | 38 | export {entry}; // loadable-extension needs this line. 39 | export default entry; 40 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoble/legoble-illustration.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoble/legoble-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | legoble-small 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoble/legoble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/legoble/legoble.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoluigi/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import legoluigiIconURL from './legoluigi.png'; 5 | import legoluigiInsetIconURL from './legoluigi-small.svg'; 6 | import legoluigiConnectionIconURL from './legoluigi-illustration.svg'; 7 | import legoluigiConnectionSmallIconURL from './legoluigi-small.svg'; 8 | import legoluigiConnectionTipIconURL from './legoluigi-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Luigi', 12 | extensionId: 'legoluigi', 13 | collaborator: 'bricklife', 14 | iconURL: legoluigiIconURL, 15 | insetIconURL: legoluigiInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: legoluigiConnectionIconURL, 29 | connectionSmallIconURL: legoluigiConnectionSmallIconURL, 30 | connectionTipIconURL: legoluigiConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoluigi/legoluigi-button-illustration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | legoluigi-button-illustration 21 | Created with Sketch. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoluigi/legoluigi-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | legoluigi-small 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoluigi/legoluigi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/legoluigi/legoluigi.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legomario/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import legomarioIconURL from './legomario.png'; 5 | import legomarioInsetIconURL from './legomario-small.svg'; 6 | import legomarioConnectionIconURL from './legomario-illustration.svg'; 7 | import legomarioConnectionSmallIconURL from './legomario-small.svg'; 8 | import legomarioConnectionTipIconURL from './legomario-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Mario', 12 | extensionId: 'legomario', 13 | collaborator: 'bricklife', 14 | iconURL: legomarioIconURL, 15 | insetIconURL: legomarioInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: legomarioConnectionIconURL, 29 | connectionSmallIconURL: legomarioConnectionSmallIconURL, 30 | connectionTipIconURL: legomarioConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legomario/legomario-button-illustration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | legomario-button-illustration 21 | Created with Sketch. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legomario/legomario-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | legomario-small 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legomario/legomario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/legomario/legomario.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legopeach/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import legopeachIconURL from './legopeach.png'; 5 | import legopeachInsetIconURL from './legopeach-small.svg'; 6 | import legopeachConnectionIconURL from './legopeach-illustration.svg'; 7 | import legopeachConnectionSmallIconURL from './legopeach-small.svg'; 8 | import legopeachConnectionTipIconURL from './legopeach-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Peach', 12 | extensionId: 'legopeach', 13 | collaborator: 'bricklife', 14 | iconURL: legopeachIconURL, 15 | insetIconURL: legopeachInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: legopeachConnectionIconURL, 29 | connectionSmallIconURL: legopeachConnectionSmallIconURL, 30 | connectionTipIconURL: legopeachConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legopeach/legopeach-button-illustration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | legomario-button-illustration 21 | Created with Sketch. 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legopeach/legopeach-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | legopeach-small 5 | Created with Sketch. 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legopeach/legopeach.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/legopeach/legopeach.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoremote/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import legoremoteIconURL from './legoremote.png'; 5 | import legoremoteInsetIconURL from './legoremote-small.svg'; 6 | import legoremoteConnectionIconURL from './legoremote-illustration.svg'; 7 | import legoremoteConnectionSmallIconURL from './legoremote-small.svg'; 8 | import legoremoteConnectionTipIconURL from './legoremote-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Powered UP Remote Control', 12 | extensionId: 'legoremote', 13 | collaborator: 'bricklife', 14 | iconURL: legoremoteIconURL, 15 | insetIconURL: legoremoteInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: legoremoteConnectionIconURL, 29 | connectionSmallIconURL: legoremoteConnectionSmallIconURL, 30 | connectionTipIconURL: legoremoteConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoremote/legoremote-illustration.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoremote/legoremote-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | legoremote-small 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/legoremote/legoremote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/legoremote/legoremote.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/poweredup/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import poweredupIconURL from './poweredup.png'; 5 | import poweredupInsetIconURL from './poweredup-small.svg'; 6 | import poweredupConnectionIconURL from './poweredup-illustration.svg'; 7 | import poweredupConnectionSmallIconURL from './poweredup-small.svg'; 8 | import poweredupConnectionTipIconURL from './poweredup-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Powered UP', 12 | extensionId: 'poweredup', 13 | collaborator: 'bricklife', 14 | iconURL: poweredupIconURL, 15 | insetIconURL: poweredupInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: poweredupConnectionIconURL, 29 | connectionSmallIconURL: poweredupConnectionSmallIconURL, 30 | connectionTipIconURL: poweredupConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/poweredup/poweredup-illustration.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/poweredup/poweredup-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | poweredup-small 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/poweredup/poweredup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/poweredup/poweredup.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeessential/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import spikeessentialIconURL from './spikeessential.png'; 5 | import spikeessentialInsetIconURL from './spikeessential-small.svg'; 6 | import spikeessentialConnectionIconURL from './spikeessential-illustration.svg'; 7 | import spikeessentialConnectionSmallIconURL from './spikeessential-small.svg'; 8 | import spikeessentialConnectionTipIconURL from './spikeessential-button-illustration.svg'; 9 | 10 | const entry = { 11 | name: 'LEGO Education SPIKE Essential', 12 | extensionId: 'spikeessential', 13 | collaborator: 'bricklife', 14 | iconURL: spikeessentialIconURL, 15 | insetIconURL: spikeessentialInsetIconURL, 16 | description: ( 17 | 21 | ), 22 | featured: true, 23 | disabled: false, 24 | bluetoothRequired: true, 25 | internetConnectionRequired: true, 26 | launchPeripheralConnectionFlow: true, 27 | useAutoScan: true, 28 | connectionIconURL: spikeessentialConnectionIconURL, 29 | connectionSmallIconURL: spikeessentialConnectionSmallIconURL, 30 | connectionTipIconURL: spikeessentialConnectionTipIconURL, 31 | connectingMessage: ( 32 | 36 | ), 37 | helpLink: 'https://scratch.mit.edu/boost' 38 | }; 39 | 40 | export {entry}; // loadable-extension needs this line. 41 | export default entry; 42 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeessential/spikeessential-illustration.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeessential/spikeessential-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | spikeessential-small 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeessential/spikeessential.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/spikeessential/spikeessential.png -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeprime/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {FormattedMessage} from 'react-intl'; 3 | 4 | import spikeprimeIconURL from './spikeprime.png'; 5 | import spikeprimeInsetIconURL from './spikeprime-small.svg'; 6 | import spikeprimeConnectionIconURL from './spikeprime-illustration.svg'; 7 | import spikeprimeConnectionSmallIconURL from './spikeprime-small.svg'; 8 | 9 | const entry = { 10 | name: 'LEGO Education SPIKE Prime (Legacy)', 11 | extensionId: 'spikeprime', 12 | collaborator: 'bricklife', 13 | iconURL: spikeprimeIconURL, 14 | insetIconURL: spikeprimeInsetIconURL, 15 | description: ( 16 | 20 | ), 21 | featured: true, 22 | disabled: false, 23 | bluetoothRequired: true, 24 | internetConnectionRequired: true, 25 | launchPeripheralConnectionFlow: true, 26 | useAutoScan: false, 27 | connectionIconURL: spikeprimeConnectionIconURL, 28 | connectionSmallIconURL: spikeprimeConnectionSmallIconURL, 29 | connectingMessage: ( 30 | 34 | ), 35 | helpLink: 'https://scratch.mit.edu/boost' 36 | }; 37 | 38 | export {entry}; // loadable-extension needs this line. 39 | export default entry; 40 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeprime/spikeprime-illustration.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | spikeprime-illustration 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeprime/spikeprime-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | spikeprime-small 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /scratch-gui/src/lib/libraries/extensions/spikeprime/spikeprime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bricklife/scratch-lego-bluetooth-extensions/3ad7de25b6c53c548bb42189d1c7533f0640c16d/scratch-gui/src/lib/libraries/extensions/spikeprime/spikeprime.png -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_controlplus/index.js: -------------------------------------------------------------------------------- 1 | const BleBaseBlocks = require('./lib/ble-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAAx4ExPAAAD6UlEQVR4Ae2aPWgUURDHZy93XhIjSTaFBILaWFhFK9FCghZaCnbaWFlYWEYEixSCaGlhLYIEEcEyIEIUPxpFIqKghRASosjlEvN5l7tdd3bzlr3lZvdld72Lyf8V997Omzcz77eze8PdI0IDARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAgZ1PwIja4tidp0M1q3KXyB5x9AajdHfB3ByRMZnPFUfHrl+YUfsVATK8ul2Zsm3bVMroHYSGMd9hFIcVxLwEZTPzzANDAzRy4gj17C02Vb03UaI339Yo39VLXeZBWi/P0MbqPB091ElXTvfTQE9H03XPX36m7z9+UZz9pos1hJJ9SR5ncnmlQpPvvtL0TMms2fxU0kVek5MXuo9tJDxe+2W24ppgeLmOAtUrS+51FDxWmP1ZdvWibo6rkPBDsi/J49xwAnGsXvPY8FjMQGfOfedJmecZIiqvWO6Q4XGz6htuL2WeO+l8rK5V3aGyf//BCzXVtL96+Ywr19UL21dGJbmaj+pVrI6O/30QkYFRpjCnCERloNJpaX9yuL/B39sp71FvEDoXunrhdVlfIwNTEm3IwFDdtyXTi9MftqSvlPlbUb3YWSZlXPjdp6sXXqf8ZtX7AFXd5xTNLa37uJT5n5sPMFz3PXzyWmtfj68NaelJSuE6MGmdJtn/1/LAO1Cv7ss6oHAdqB7nsDxrv1nZ8zPQMdhQ96m6KytHYTuS/TR1WthHK64DGdgKdzvPBwCmvKcAmBKgcfP2IzvOhtnXQ+fPHaPOzj1xqpHz6+tVejbxkeYXliP1kkxK71TJVlaxaGUgb5g3zk6TtqwCTuo/uC7LWPwMLM+9Cvrwx7lcgfYNDFMu3+3L0gys2iotlabIsrxfbdLY4rX9g6cSm+BY1L6k/UvGld/YDOSN/il9InaWtmUNL008KpY0NnhtsA4UbdlWlRZ/vxfnt8PEVjMoq5hjMzArRzvVjp+B6plu151UgFUc6rpVfVK/yMCUd8j/W1PVg9slA9sdh8RVZeqtG5dcdshAiZSmHAA1QUlqACiR0ZQDoCYoSQ0AJTKacgDUBCWpAaBERlMOgJqgJDUAlMhoygFQE5SkBoASGU15EKBzBtg5cZlrfhJV015qNavu/W3Q7jiabSQQk8uKdQIAjUkWdPcdbivEWnWBw2h7HG4QgQ+Gx2y85rHisf97IJ8+dw6Vny0UTbN3//FNxfZ1Thy0HeIIE9g8ZD6q5H4G8qlzPn3unEMfdyb9FFWK6JmJMR48oQ8mIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACILBbCPwFpqc7sJw6T5QAAAAASUVORK5CYII='; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/controlplus.mjs'; 8 | 9 | class Scratch3ControlPlusBlocks extends BleBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'controlplus'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3ControlPlusBlocks.EXTENSION_ID, 0x80)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | getInfo() { 33 | this.setupTranslations(formatMessage); 34 | 35 | return { 36 | id: Scratch3ControlPlusBlocks.EXTENSION_ID, 37 | name: 'CONTROL+', 38 | extensionURL: Scratch3ControlPlusBlocks.extensionURL, 39 | blockIconURI: blockIconURI, 40 | showStatusButton: true, 41 | blocks: this.getBlocks(formatMessage), 42 | menus: this.getMenus(formatMessage) 43 | }; 44 | } 45 | } 46 | 47 | exports.blockClass = Scratch3ControlPlusBlocks; 48 | module.exports = Scratch3ControlPlusBlocks; 49 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_controlplus/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_duplotrain/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoble/index.js: -------------------------------------------------------------------------------- 1 | const BleBaseBlocks = require('./lib/ble-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAAx4ExPAAAC2ElEQVR4Ae3avW4TQRDA8TuDJcvCjQtLuEQiTXqgtngCoEwHCGh4AUv+kvwMgEQ63oIQWa6Agi5l6DAP4Ciy5eKYOXGR45Vud/DZ6OB/Rc7end3b+d36EjkTRRwIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIILBbgTh0+iRJ4tFo9EzOT2XMoZxvhY4tQ1wcxxeyzjM5H/d6vfdyTkLWHQQ4Ho9vr1arD4LWCZm07DGCd1qtVo+63e5PXy43fQG684bDYYrXaDSSe/cfxO12O6rX676hpeq/vLyMZrNZ9PXL52Q+n3d+b5iHvp14w5dlpVJ5LoivFe/R4ydxq9WK5O74hpWuX3NqNpvR3YOD+Pv5ebJcLu9Mp9Mfk8nkW14ylbxO7RM8feZFuvNqtZq+/KcPzVFz1SSz3PMS9gLK4EOdQD+2/8uxlmuae17eIc/A9Lft5jPv3ds3efOWru/Fy1dXa85ylR3o/UsjZAdeTcwLVwBA18TUAqCJyw0G0DUxtQBo4nKDAXRNTC0AmrjcYABdE1MLgCYuNxhA18TUAqCJyw0G0DUxtQBo4nKDAXRNTC0AmrjcYABdE1MLgCYuN9j7jbQ7JL9l/Zvd/Mi/01v0N+nswC3vI4AAbimw5fDCn4Gb6yn6mbM5v+/9rp/JfIR9d8DT792BUhtyof8f1dqR7P+lOmfIndUxeugcg8Ggkb7Z0w+53ryIdfuWG7IDz3QSLbyxHmtj0jms47eM38u6vYCye441Ea1aWiwWwTlprI7RAdkcwYMLCMyuuet1e+sD5WOg5W0ncu6ElLdtlIlJHvFpv9/3lokVYHZtin2tO+QZmEiB5ZHWy2nd3KeTj9cWmvMmxdNCRXkRVO2ZM5e5S6+5j3V7d2C2cr2jISW+svA/KpXNrlP0uazrLtqB+RBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAor8Av1TQfBCyGec4AAAAASUVORK5CYII='; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/legoble.mjs'; 8 | 9 | class Scratch3LegoBleBlocks extends BleBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'legoble'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3LegoBleBlocks.EXTENSION_ID)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | get hasAdvancedBlocks() { 33 | return true; 34 | } 35 | 36 | getInfo() { 37 | this.setupTranslations(formatMessage); 38 | 39 | return { 40 | id: Scratch3LegoBleBlocks.EXTENSION_ID, 41 | name: 'LEGO BLE', 42 | extensionURL: Scratch3LegoBleBlocks.extensionURL, 43 | blockIconURI: blockIconURI, 44 | showStatusButton: true, 45 | blocks: this.getBlocks(formatMessage), 46 | menus: this.getMenus(formatMessage) 47 | }; 48 | } 49 | } 50 | 51 | exports.blockClass = Scratch3LegoBleBlocks; 52 | module.exports = Scratch3LegoBleBlocks; 53 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoble/lib/color.js: -------------------------------------------------------------------------------- 1 | const Color = { 2 | BLACK: 0, 3 | PINK: 1, 4 | PURPLE: 2, 5 | BLUE: 3, 6 | LIGHT_BLUE: 4, 7 | LIGHT_GREEN: 5, 8 | GREEN: 6, 9 | YELLOW: 7, 10 | ORANGE: 8, 11 | RED: 9, 12 | WHITE: 10, 13 | NONE: -1, 14 | }; 15 | 16 | module.exports = Color; 17 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoble/lib/device.js: -------------------------------------------------------------------------------- 1 | const MathUtil = require('../../../util/math-util'); 2 | 3 | const IOType = require('./io-type'); 4 | 5 | class GenericDevice { 6 | 7 | constructor(ioType) { 8 | this._ioType = ioType; 9 | this._inputValues = {}; 10 | } 11 | 12 | get ioType() { 13 | return this._ioType; 14 | } 15 | 16 | get mode() { 17 | switch (this._ioType) { 18 | case IOType.MEDIUM_LINEAR_MOTOR: 19 | case IOType.MOVE_HUB_MOTOR: 20 | case IOType.TECHNIC_LARGE_MOTOR: 21 | case IOType.TECHNIC_XL_MOTOR: 22 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR: 23 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR: 24 | case IOType.TECHNIC_SMALL_ANGULAR_MOTOR: 25 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR_GRAY: 26 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR_GRAY: 27 | return 2; // Relative Position 28 | case IOType.TILT_SENSOR: 29 | return 0; // Tilt X, Y 30 | case IOType.MOTION_SENSOR: 31 | return 0; // Distance 32 | case IOType.COLOR_DISTANCE_SENSOR: 33 | return 8; // Color and Distance 34 | case IOType.MOVE_HUB_TILT_SENSOR: 35 | return 0; // Tilt X, Y 36 | case IOType.DUPLO_TRAIN_BASE_SPEAKER: 37 | return 1; // Sound 38 | case IOType.DUPLO_TRAIN_BASE_COLOR_SENSOR: 39 | return 1; // Color 40 | case IOType.DUPLO_TRAIN_BASE_SPEEDOMETER: 41 | return 1; // Driving Distance 42 | case IOType.REMOTE_POWER_CONTROL_BUTTON: 43 | return 0; // Button 44 | case IOType.TECHNIC_HUB_TILT_SENSOR: 45 | return 0; // Tilt X, Y, Z 46 | case IOType.TECHNIC_COLOR_SENSOR: 47 | return 0; // Color 48 | case IOType.TECHNIC_DISTANCE_SENSOR: 49 | return 0; // Distance 50 | case IOType.TECHNIC_FORCE_SENSOR: 51 | return 0; // Force 52 | case IOType.MARIO_COLOR_BARCODE_SENSOR: 53 | return 0 54 | case IOType.MARIO_PANTS: 55 | return 0 56 | default: 57 | return null; 58 | } 59 | } 60 | 61 | get inputValues() { 62 | return this._inputValues; 63 | } 64 | 65 | updateInputValues(data) { 66 | if (data.length == 0) { 67 | this._inputValues = {}; 68 | return; 69 | } 70 | 71 | const buffer = Buffer.from(data); 72 | 73 | switch (this._ioType) { 74 | case IOType.MEDIUM_LINEAR_MOTOR: 75 | case IOType.MOVE_HUB_MOTOR: 76 | case IOType.TECHNIC_LARGE_MOTOR: 77 | case IOType.TECHNIC_XL_MOTOR: 78 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR: 79 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR: 80 | case IOType.TECHNIC_SMALL_ANGULAR_MOTOR: 81 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR_GRAY: 82 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR_GRAY: 83 | this._inputValues = { 84 | relativePosition: buffer.readInt32LE(0) 85 | }; 86 | break; 87 | 88 | case IOType.TILT_SENSOR: 89 | this._inputValues = { 90 | tiltX: buffer.readInt8(0), 91 | tiltY: buffer.readInt8(1) 92 | }; 93 | break; 94 | 95 | case IOType.MOTION_SENSOR: 96 | this._inputValues = { 97 | distance: buffer.readInt8(0) 98 | }; 99 | break; 100 | 101 | case IOType.COLOR_DISTANCE_SENSOR: 102 | this._inputValues = { 103 | color: buffer.readInt8(0), 104 | distance: buffer.readInt8(1) 105 | }; 106 | break; 107 | 108 | case IOType.MOVE_HUB_TILT_SENSOR: 109 | this._inputValues = { 110 | tiltX: buffer.readInt8(0), 111 | tiltY: buffer.readInt8(1) 112 | }; 113 | break; 114 | 115 | case IOType.DUPLO_TRAIN_BASE_COLOR_SENSOR: 116 | const value = buffer.readInt8(0); 117 | if (value > -1) { 118 | this._inputValues = { 119 | color: value 120 | }; 121 | setTimeout(() => { 122 | this._inputValues = { 123 | color: -1 124 | }; 125 | }, 100); 126 | } 127 | break; 128 | 129 | case IOType.DUPLO_TRAIN_BASE_SPEEDOMETER: 130 | this._inputValues = { 131 | drivingDistance: buffer.readInt32LE(0) 132 | }; 133 | break; 134 | 135 | case IOType.REMOTE_POWER_CONTROL_BUTTON: 136 | this._inputValues = { 137 | button: buffer.readInt8(0) 138 | }; 139 | break; 140 | 141 | case IOType.TECHNIC_HUB_TILT_SENSOR: 142 | this._inputValues = { 143 | tiltX: buffer.readInt16LE(4), 144 | tiltY: buffer.readInt16LE(2), 145 | tiltZ: buffer.readInt16LE(0) 146 | }; 147 | break; 148 | 149 | case IOType.TECHNIC_COLOR_SENSOR: 150 | this._inputValues = { 151 | color: buffer.readInt8(0) 152 | }; 153 | break; 154 | 155 | case IOType.TECHNIC_DISTANCE_SENSOR: 156 | this._inputValues = { 157 | distance: buffer.readInt16LE(0) 158 | }; 159 | break; 160 | 161 | case IOType.TECHNIC_FORCE_SENSOR: 162 | this._inputValues = { 163 | force: buffer.readInt8(0) 164 | }; 165 | break; 166 | 167 | case IOType.MARIO_COLOR_BARCODE_SENSOR: 168 | this._inputValues = { 169 | barcode: buffer.readInt16LE(0), 170 | color: buffer.readInt16LE(2) 171 | }; 172 | break; 173 | 174 | case IOType.MARIO_PANTS: 175 | this._inputValues = { 176 | pants: buffer.readInt8(0) 177 | }; 178 | break; 179 | 180 | default: 181 | this._inputValues = {}; 182 | break; 183 | } 184 | 185 | this._inputValues.bytes = buffer; 186 | } 187 | } 188 | 189 | class Motor extends GenericDevice { 190 | 191 | constructor(ioType) { 192 | super(ioType); 193 | 194 | switch (ioType) { 195 | case IOType.MEDIUM_LINEAR_MOTOR: 196 | case IOType.MOVE_HUB_MOTOR: 197 | this._canUseSpeed = true; 198 | this._canUsePosition = false; 199 | this._speed = 75; 200 | break; 201 | 202 | case IOType.TECHNIC_LARGE_MOTOR: 203 | case IOType.TECHNIC_XL_MOTOR: 204 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR: 205 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR: 206 | case IOType.TECHNIC_SMALL_ANGULAR_MOTOR: 207 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR_GRAY: 208 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR_GRAY: 209 | this._canUseSpeed = true; 210 | this._canUsePosition = true; 211 | this._speed = 75; 212 | break; 213 | 214 | default: 215 | this._canUseSpeed = false; 216 | this._canUsePosition = false; 217 | this._speed = 0; 218 | } 219 | } 220 | 221 | get canUseSpeed() { 222 | return this._canUseSpeed; 223 | } 224 | 225 | get canUsePosition() { 226 | return this._canUsePosition; 227 | } 228 | 229 | get speed() { 230 | return this._speed; 231 | } 232 | 233 | set speed(value) { 234 | if (this._canUseSpeed) { 235 | this._speed = MathUtil.clamp(value, -100, 100); 236 | } 237 | } 238 | } 239 | 240 | const createDevice = function (ioType) { 241 | switch (ioType) { 242 | case IOType.SIMPLE_MEDIUM_LINEAR_MOTOR: 243 | case IOType.TRAIN_MOTOR: 244 | case IOType.LIGHT: 245 | case IOType.MEDIUM_LINEAR_MOTOR: 246 | case IOType.MOVE_HUB_MOTOR: 247 | case IOType.DUPLO_TRAIN_BASE_MOTOR: 248 | case IOType.TECHNIC_LARGE_MOTOR: 249 | case IOType.TECHNIC_XL_MOTOR: 250 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR: 251 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR: 252 | case IOType.TECHNIC_SMALL_ANGULAR_MOTOR: 253 | case IOType.TECHNIC_MEDIUM_ANGULAR_MOTOR_GRAY: 254 | case IOType.TECHNIC_LARGE_ANGULAR_MOTOR_GRAY: 255 | return new Motor(ioType); 256 | 257 | default: 258 | return new GenericDevice(ioType); 259 | } 260 | } 261 | 262 | module.exports.GenericDevice = GenericDevice; 263 | module.exports.Motor = Motor; 264 | module.exports.createDevice = createDevice; 265 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoble/lib/io-type.js: -------------------------------------------------------------------------------- 1 | const IOType = { 2 | SIMPLE_MEDIUM_LINEAR_MOTOR: 0x01, 3 | TRAIN_MOTOR: 0x02, 4 | BUTTION: 0x05, 5 | LIGHT: 0x08, 6 | VOLTAGE: 0x14, 7 | CURRENT: 0x15, 8 | PIEZO_TONE: 0x16, 9 | RGB_LIGHT: 0x17, 10 | TILT_SENSOR: 0x22, 11 | MOTION_SENSOR: 0x23, 12 | COLOR_DISTANCE_SENSOR: 0x25, 13 | MEDIUM_LINEAR_MOTOR: 0x26, 14 | MOVE_HUB_MOTOR: 0x27, 15 | MOVE_HUB_TILT_SENSOR: 0x28, 16 | DUPLO_TRAIN_BASE_MOTOR: 0x29, 17 | DUPLO_TRAIN_BASE_SPEAKER: 0x2a, 18 | DUPLO_TRAIN_BASE_COLOR_SENSOR: 0x2b, 19 | DUPLO_TRAIN_BASE_SPEEDOMETER: 0x2c, 20 | TECHNIC_LARGE_MOTOR: 0x2e, 21 | TECHNIC_XL_MOTOR: 0x2f, 22 | TECHNIC_MEDIUM_ANGULAR_MOTOR: 0x30, 23 | TECHNIC_LARGE_ANGULAR_MOTOR: 0x31, 24 | REMOTE_POWER_CONTROL_BUTTON: 0x37, 25 | TECHNIC_HUB_TILT_SENSOR: 0x3b, 26 | TECHNIC_COLOR_SENSOR: 0x3d, 27 | TECHNIC_DISTANCE_SENSOR: 0x3e, 28 | TECHNIC_FORCE_SENSOR: 0x3f, 29 | TECHNIC_SMALL_ANGULAR_MOTOR: 0x41, 30 | MARIO_COLOR_BARCODE_SENSOR: 0x49, 31 | MARIO_PANTS: 0x4a, 32 | TECHNIC_MEDIUM_ANGULAR_MOTOR_GRAY: 0x4b, 33 | TECHNIC_LARGE_ANGULAR_MOTOR_GRAY: 0x4c, 34 | }; 35 | 36 | module.exports = IOType; 37 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoble/lib/setup-translations.js: -------------------------------------------------------------------------------- 1 | const setupTranslations = function (formatMessage, extTranslations = {}) { 2 | const localeSetup = formatMessage.setup(); 3 | 4 | const translations = { 5 | 'en': { 6 | 'legobluetooth.motorPWM': '[PORT] start motor at [POWER] % power', 7 | 'legobluetooth.motorStop': '[PORT] stop motor', 8 | 'legobluetooth.motorRunFor': '[PORT] run [DIRECTION] for [VALUE] [UNIT]', 9 | 'legobluetooth.motorGoDirectionToPosition': '[PORT] go [DIRECTION] to position [POSITION]', 10 | 'legobluetooth.motorStart': '[PORT] start motor [DIRECTION]', 11 | 'legobluetooth.motorSetSpeed': '[PORT] set speed to [SPEED] %', 12 | 'legobluetooth.getRelativePosition': '[PORT] relative position', 13 | 'legobluetooth.getPosition': '[PORT] position', 14 | 'legobluetooth.motorResetRelativePosition': '[PORT] reset relative position to [RELATIVE_POSITION]', 15 | 16 | 'legobluetooth.displayImageFor': 'turn on [MATRIX] for [DURATION] seconds', 17 | 'legobluetooth.displayImage': 'turn on [MATRIX]', 18 | 'legobluetooth.displayText': 'write [TEXT]', 19 | 'legobluetooth.displayClear': 'turn off pixels', 20 | 'legobluetooth.displaySetBrightness': 'set pixel brightness to [BRIGHTNESS] %', 21 | 'legobluetooth.displaySetPixel': 'set pixel at [X] , [Y] to [BRIGHTNESS] %', 22 | 'legobluetooth.centerButtonLights': 'set center button light to [COLOR]', 23 | 'legobluetooth.ultrasonicLightUp': '[PORT] light up [LIGHT0] [LIGHT1] [LIGHT2] [LIGHT3]', 24 | 25 | 'legobluetooth.getColor': '[PORT] color', 26 | 'legobluetooth.getDistance': '[PORT] distance', 27 | 'legobluetooth.getForce': '[PORT] force', 28 | 'legobluetooth.getTilt': '[PORT] tilt [XY]', 29 | 'legobluetooth.setHubLEDColor': 'set hub LED color to [COLOR]', 30 | 'legobluetooth.getHubTilt': 'hub tilt [XYZ]', 31 | 'legobluetooth.getAngle': '[AXIS] angle', 32 | 33 | 'legobluetooth.getName': 'name', 34 | 'legobluetooth.getFirmwareVersion': 'firmware version', 35 | 'legobluetooth.getBatteryLevel': 'battery level', 36 | 37 | 'legobluetooth.rotations': 'rotations', 38 | 'legobluetooth.degrees': 'degrees', 39 | 'legobluetooth.seconds': 'seconds', 40 | 'legobluetooth.shortestPath': 'shortest', 41 | 'legobluetooth.clockwise': 'clockwise', 42 | 'legobluetooth.counterclockwise': 'counterclockwise', 43 | 44 | 'legobluetooth.black': '(0) Black', 45 | 'legobluetooth.pink': '(1) Pink', 46 | 'legobluetooth.purple': '(2) Purple', 47 | 'legobluetooth.blue': '(3) Blue', 48 | 'legobluetooth.lightBlue': '(4) Light blue', 49 | 'legobluetooth.lightGreen': '(5) Light green', 50 | 'legobluetooth.green': '(6) Green', 51 | 'legobluetooth.yellow': '(7) Yellow', 52 | 'legobluetooth.orange': '(8) Orange', 53 | 'legobluetooth.red': '(9) Red', 54 | 'legobluetooth.white': '(10) White', 55 | 'legobluetooth.noColor': '(-1) No color', 56 | 57 | 'legobluetooth.pitch': 'pitch', 58 | 'legobluetooth.roll': 'roll', 59 | 'legobluetooth.yaw': 'yaw', 60 | }, 61 | 'it': { 62 | }, 63 | 'ja': { 64 | 'legobluetooth.motorPWM': '[PORT] モーターを [POWER] %のパワーで回す', 65 | 'legobluetooth.motorStop': '[PORT] モーターを止める', 66 | 'legobluetooth.motorRunFor': '[PORT] モーターを [DIRECTION] 方向に [VALUE] [UNIT] 回す', 67 | 'legobluetooth.motorGoDirectionToPosition': '[PORT] モーターを [DIRECTION] で位置 [POSITION] まで回す', 68 | 'legobluetooth.motorStart': '[PORT] モーターを [DIRECTION] 方向に回す', 69 | 'legobluetooth.motorSetSpeed': '[PORT] スピードを [SPEED] %にする', 70 | 'legobluetooth.getRelativePosition': '[PORT] 相対位置', 71 | 'legobluetooth.getPosition': '[PORT] 位置', 72 | 'legobluetooth.motorResetRelativePosition': '[PORT] 相対位置を [RELATIVE_POSITION] にリセットする', 73 | 74 | 'legobluetooth.displayImageFor': '[MATRIX] を [DURATION] 秒間オンにする', 75 | 'legobluetooth.displayImage': '[MATRIX] をオンにする', 76 | 'legobluetooth.displayText': '[TEXT] を表示する', 77 | 'legobluetooth.displayClear': 'すべてのピクセルをオフにする', 78 | 'legobluetooth.displaySetBrightness': 'ピクセルの明るさを [BRIGHTNESS] %にする', 79 | 'legobluetooth.displaySetPixel': '[X] , [Y] のピクセルの明るさを [BRIGHTNESS] %にする', 80 | 'legobluetooth.centerButtonLights': 'センターボタンのライトを [COLOR] にする', 81 | 'legobluetooth.ultrasonicLightUp': '[PORT] を [LIGHT0] [LIGHT1] [LIGHT2] [LIGHT3] でライトアップする', 82 | 83 | 'legobluetooth.getColor': '[PORT] 色', 84 | 'legobluetooth.getDistance': '[PORT] 距離', 85 | 'legobluetooth.getForce': '[PORT] 圧力', 86 | 'legobluetooth.getTilt': '[PORT] 傾き [XY]', 87 | 'legobluetooth.setHubLEDColor': 'ハブのLEDを [COLOR] にする', 88 | 'legobluetooth.getHubTilt': 'ハブの傾き [XYZ]', 89 | 'legobluetooth.getAngle': '[AXIS] 角', 90 | 91 | 'legobluetooth.getName': '名前', 92 | 'legobluetooth.getFirmwareVersion': 'ファームウェアバージョン', 93 | 'legobluetooth.getBatteryLevel': '電池残量', 94 | 95 | 'legobluetooth.rotations': '回転', 96 | 'legobluetooth.degrees': '度', 97 | 'legobluetooth.seconds': '秒', 98 | 'legobluetooth.shortestPath': '最短経路', 99 | 'legobluetooth.clockwise': '時計回り', 100 | 'legobluetooth.counterclockwise': '反時計回り', 101 | 102 | 'legobluetooth.black': '(0) 黒', 103 | 'legobluetooth.pink': '(1) ピンク', 104 | 'legobluetooth.purple': '(2) 紫', 105 | 'legobluetooth.blue': '(3) 青', 106 | 'legobluetooth.lightBlue': '(4) 水色', 107 | 'legobluetooth.lightGreen': '(5) 明るい緑', 108 | 'legobluetooth.green': '(6) 緑', 109 | 'legobluetooth.yellow': '(7) 黄色', 110 | 'legobluetooth.orange': '(8) オレンジ', 111 | 'legobluetooth.red': '(9) 赤', 112 | 'legobluetooth.white': '(10) 白', 113 | 'legobluetooth.noColor': '(-1) 色なし', 114 | 115 | 'legobluetooth.pitch': 'ピッチ', 116 | 'legobluetooth.roll': 'ロール', 117 | 'legobluetooth.yaw': 'ヨー', 118 | }, 119 | 'ja-Hira': { 120 | 'legobluetooth.motorPWM': '[PORT] モーターを [POWER] %のパワーでまわす', 121 | 'legobluetooth.motorStop': '[PORT] モーターをとめる', 122 | 'legobluetooth.motorRunFor': '[PORT] モーターを [DIRECTION] ほうこうに [VALUE] [UNIT] まわす', 123 | 'legobluetooth.motorGoDirectionToPosition': '[PORT] モーターを [DIRECTION] でいち [POSITION] までまわす', 124 | 'legobluetooth.motorStart': '[PORT] モーターを [DIRECTION] ほうこうにまわす', 125 | 'legobluetooth.motorSetSpeed': '[PORT] スピードを [SPEED] %にする', 126 | 'legobluetooth.getRelativePosition': '[PORT] そうたいいち', 127 | 'legobluetooth.getPosition': '[PORT] いち', 128 | 'legobluetooth.motorResetRelativePosition': '[PORT] そうたいいちを [RELATIVE_POSITION] にリセットする', 129 | 130 | 'legobluetooth.displayImageFor': '[MATRIX] を [DURATION] びょうかんオンにする', 131 | 'legobluetooth.displayImage': '[MATRIX] をオンにする', 132 | 'legobluetooth.displayText': '[TEXT] をひょうじする', 133 | 'legobluetooth.displayClear': 'すべてのピクセルをオフにする', 134 | 'legobluetooth.displaySetBrightness': 'ピクセルのあかるさを [BRIGHTNESS] %にする', 135 | 'legobluetooth.displaySetPixel': '[X] , [Y] のピクセルのあかるさを [BRIGHTNESS] %にする', 136 | 'legobluetooth.centerButtonLights': 'センターボタンのライトを [COLOR] にする', 137 | 'legobluetooth.ultrasonicLightUp': '[PORT] を [LIGHT0] [LIGHT1] [LIGHT2] [LIGHT3] でライトアップする', 138 | 139 | 'legobluetooth.getColor': '[PORT] いろ', 140 | 'legobluetooth.getDistance': '[PORT] きょり', 141 | 'legobluetooth.getForce': '[PORT] あつりょく', 142 | 'legobluetooth.getTilt': '[PORT] かたむき [XY]', 143 | 'legobluetooth.setHubLEDColor': 'ハブのLEDを [COLOR] にする', 144 | 'legobluetooth.getHubTilt': 'ハブのかたむき [XYZ]', 145 | 'legobluetooth.getAngle': '[AXIS] かく', 146 | 147 | 'legobluetooth.getName': 'なまえ', 148 | 'legobluetooth.getFirmwareVersion': 'ファームウェアバージョン', 149 | 'legobluetooth.getBatteryLevel': 'でんちざんりょう', 150 | 151 | 'legobluetooth.rotations': 'かいてん', 152 | 'legobluetooth.degrees': 'ど', 153 | 'legobluetooth.seconds': 'びょう', 154 | 'legobluetooth.shortestPath': 'さいたんきょり', 155 | 'legobluetooth.clockwise': 'とけいまわり', 156 | 'legobluetooth.counterclockwise': 'はんとけいまわり', 157 | 158 | 'legobluetooth.black': '(0) くろ', 159 | 'legobluetooth.pink': '(1) ピンク', 160 | 'legobluetooth.purple': '(2) むらさき', 161 | 'legobluetooth.blue': '(3) あお', 162 | 'legobluetooth.lightBlue': '(4) みずいろ', 163 | 'legobluetooth.lightGreen': '(5) あかるいみどり', 164 | 'legobluetooth.green': '(6) みどり', 165 | 'legobluetooth.yellow': '(7) きいろ', 166 | 'legobluetooth.orange': '(8) オレンジ', 167 | 'legobluetooth.red': '(9) あか', 168 | 'legobluetooth.white': '(10) しろ', 169 | 'legobluetooth.noColor': '(-1) いろなし', 170 | 171 | 'legobluetooth.pitch': 'ピッチ', 172 | 'legobluetooth.roll': 'ロール', 173 | 'legobluetooth.yaw': 'ヨー', 174 | } 175 | }; 176 | 177 | for (const locale in translations) { 178 | if (extTranslations[locale]) { 179 | Object.assign(translations[locale], extTranslations[locale]); 180 | } 181 | 182 | if (!localeSetup.translations[locale]) { 183 | localeSetup.translations[locale] = {}; 184 | } 185 | Object.assign(localeSetup.translations[locale], translations[locale]); 186 | } 187 | }; 188 | 189 | module.exports = setupTranslations; 190 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoluigi/index.js: -------------------------------------------------------------------------------- 1 | const MarioBaseBlocks = require('./lib/mario-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,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'; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/legoluigi.mjs'; 8 | 9 | class Scratch3LegoLuigiBlocks extends MarioBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'legoluigi'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3LegoLuigiBlocks.EXTENSION_ID, 0x44)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | getInfo() { 33 | this.setupTranslations(formatMessage); 34 | 35 | return { 36 | id: Scratch3LegoLuigiBlocks.EXTENSION_ID, 37 | name: 'LEGO Luigi', 38 | extensionURL: Scratch3LegoLuigiBlocks.extensionURL, 39 | blockIconURI: blockIconURI, 40 | showStatusButton: true, 41 | blocks: this.getBlocks(formatMessage), 42 | menus: this.getMenus(formatMessage) 43 | }; 44 | } 45 | } 46 | 47 | exports.blockClass = Scratch3LegoLuigiBlocks; 48 | module.exports = Scratch3LegoLuigiBlocks; 49 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoluigi/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legomario/index.js: -------------------------------------------------------------------------------- 1 | const MarioBaseBlocks = require('./lib/mario-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,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'; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/legomario.mjs'; 8 | 9 | class Scratch3LegoMarioBlocks extends MarioBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'legomario'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3LegoMarioBlocks.EXTENSION_ID, 0x43)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | getInfo() { 33 | this.setupTranslations(formatMessage); 34 | 35 | return { 36 | id: Scratch3LegoMarioBlocks.EXTENSION_ID, 37 | name: 'LEGO Mario', 38 | extensionURL: Scratch3LegoMarioBlocks.extensionURL, 39 | blockIconURI: blockIconURI, 40 | showStatusButton: true, 41 | blocks: this.getBlocks(formatMessage), 42 | menus: this.getMenus(formatMessage) 43 | }; 44 | } 45 | } 46 | 47 | exports.blockClass = Scratch3LegoMarioBlocks; 48 | module.exports = Scratch3LegoMarioBlocks; 49 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legomario/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legopeach/index.js: -------------------------------------------------------------------------------- 1 | const MarioBaseBlocks = require('./lib/mario-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,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'; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/legopeach.mjs'; 8 | 9 | class Scratch3LegoPeachBlocks extends MarioBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'legopeach'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3LegoPeachBlocks.EXTENSION_ID, 0x45)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | getInfo() { 33 | this.setupTranslations(formatMessage); 34 | 35 | return { 36 | id: Scratch3LegoPeachBlocks.EXTENSION_ID, 37 | name: 'LEGO Peach', 38 | extensionURL: Scratch3LegoPeachBlocks.extensionURL, 39 | blockIconURI: blockIconURI, 40 | showStatusButton: true, 41 | blocks: this.getBlocks(formatMessage), 42 | menus: this.getMenus(formatMessage) 43 | }; 44 | } 45 | } 46 | 47 | exports.blockClass = Scratch3LegoPeachBlocks; 48 | module.exports = Scratch3LegoPeachBlocks; 49 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legopeach/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_legoremote/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_poweredup/index.js: -------------------------------------------------------------------------------- 1 | const BleBaseBlocks = require('./lib/ble-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAASKG51AAAByElEQVR4Ae3YP07DMBiG8QS4ACdBYmQtlwCmzhwAkICFSvzZmZkoCxyBlZ2TcIOEtJKrqlL6fGlqCdonS9y88WfnV0eWUhQeCiiggAIKKKCAAgoooIACCiiggAIKKKCAAgoooIACCiiQV6DsUv728W1QVfWwLIvduiw/Rhcn75P+m3o9YrMXuSndU9XVU9M+rOuiaOSPm/YUcFOvp+dedsYVeH3/2nBt7zG6OltqtLO9NOt58vArfHd5up4RF6rcPIynV3LVXxgu/DPNizqEASeFIkUTRJd7aZJ/OfcV7vnvdFqBaayjg/3UnJ2/vn9m7flGl3vn+/2X9tIdZvIQ7sLuwlkXc/gVPh8Oskzk+eVzWjdX/VUnneZF/d1ESAhyAQGIYgFJCHIBAYhiAUkIcgEBiGIBSQhyAQGIYgFJCHIBAYhiAUkIcgEBiGIBSQhyAQGIYgFJCPLw98Do9zEYrzXOXb914J6BK7AnYHgF5vpinFZervqr+qR5UX9XIAlBLiAAUSwgCUEuIABRLCAJQR7ehaO7EozXGueu3zpwz8AV2BPQ7goooIACCiiggAIKKKCAAgoooIACCiiggAIKKKCAAgookFvgF1zdypav+pVRAAAAAElFTkSuQmCC'; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/poweredup.mjs'; 8 | 9 | class Scratch3PoweredUpBlocks extends BleBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'poweredup'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3PoweredUpBlocks.EXTENSION_ID, 0x41)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | get externalPorts() { 33 | return ['A', 'B']; 34 | } 35 | 36 | get multipleExternalPorts() { 37 | return ['A', 'B', 'A+B']; 38 | } 39 | 40 | get hasInternalTiltSensorBlocks() { 41 | return false; 42 | } 43 | 44 | getInfo() { 45 | this.setupTranslations(formatMessage); 46 | 47 | return { 48 | id: Scratch3PoweredUpBlocks.EXTENSION_ID, 49 | name: 'Powered UP', 50 | extensionURL: Scratch3PoweredUpBlocks.extensionURL, 51 | blockIconURI: blockIconURI, 52 | showStatusButton: true, 53 | blocks: this.getBlocks(formatMessage), 54 | menus: this.getMenus(formatMessage) 55 | }; 56 | } 57 | } 58 | 59 | exports.blockClass = Scratch3PoweredUpBlocks; 60 | module.exports = Scratch3PoweredUpBlocks; 61 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_poweredup/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_spikeessential/index.js: -------------------------------------------------------------------------------- 1 | const BleBaseBlocks = require('./lib/ble-base-blocks'); 2 | const Hub = require('./lib/hub'); 3 | 4 | const blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAUKADAAQAAAABAAAAUAAAAAAx4ExPAAADMElEQVR4Ae3bT2gTQRQG8LetpI1GK4iUKhRSoQhVMfRealAEr4J40It/ToLgSVFEPHjoqSdv6kVBQXoVBIk2IHjRQjUVBKkKKh7VaBrFrvttM5ApSX3TyHYSvoGwmc2b2X2/ziS7dEeEhQIUoAAFKEABClCAAhSgAAUoQAEKUIACFKAABSiQhEDgepAwDIMrE/dORduTUduRUMKMax8+xQcSlKPzKQVBcPPq+aM3om3ocn5OgNcmpwYq1eodCcO8y0HaJjYICumenmOXzh3+rD3nddpAjLzLE3djvL5N6+XgvpxkB/sls6FX24WXceUfCzL/4Ys8fDwjX7/9zGOARLnu147Ebm1WYXrn6ajjs8A7c+KQbB/YIqmU2l97mMTjkEP/1s2S2z0ks3PvpFr9nZ1+Wvo4/WjqheZkujRBiKl958UjL92b0jZrmzjkhFmFYnLVnLwaMOpsBB1i2nZqqcstzlWTpxrQ/Nq2+3feSigmN5PrSrHmMzWgacCtLUBA28O5RkBnMrsBAW0P5xoBncnsBgS0PZxrBHQmsxsQ0PZwrhHQmcxuQEDbw7lGQGcyuwEBbQ/nGgGdyewGBLQ9nGsEdCazGxDQ9nCuEdCZzG7gzX+F/iwuSqE4KzOv5uV7uWKfZa22MZOW3K6s5Mf2SHeXH397bwCBV3w21xDO7ASsiTkwvtfsXtOtN4AYeSjHj4zL8NC2hihv3n6S2/efxKPUF0A/5kHEZaZtMzyIDu9YgjWxDZUT3ukNYMJ5/7fDEbBFSgISsEWBFptzBHYKIC6SUXCp0qyYz0xss7gk93tzHYg7DFwk4zrvXwWxvhRvAHF7hqK9lSPgMgHc2+LuAq+xi8+XfbpULV4Ybbh/LXfyR6RFfTVg7Wl2wTPFSZW+wVHBK6licjO5ao6rBow6K6FDPJDdqaUutzhXTZ5qQKyjQId4mr2y8EvTd1vFICfkhmJy1SSgBsQilKjnQrQUQK7feiAvX79PdDprkllNDKYtckFOyA05xrkqO+NCm3qoVSy0cQLEsaIlAFzqVY/O9xSgAAUoQAEKUIACFKAABShAAQpQgAIUoAAFKECBRAT+AigB5y5rdsAFAAAAAElFTkSuQmCC'; 5 | 6 | let formatMessage = require('format-message'); 7 | let extensionURL = 'https://bricklife.com/scratch-gui/xcratch/spikeessential.mjs'; 8 | 9 | class Scratch3SpikeEssentialBlocks extends BleBaseBlocks { 10 | 11 | static get EXTENSION_ID() { 12 | return 'spikeessential'; 13 | } 14 | 15 | static get extensionURL() { 16 | return extensionURL; 17 | } 18 | 19 | static set extensionURL(url) { 20 | extensionURL = url; 21 | } 22 | 23 | constructor(runtime) { 24 | super(new Hub(runtime, Scratch3SpikeEssentialBlocks.EXTENSION_ID, 0x83)); 25 | 26 | if (runtime.formatMessage) { 27 | // Replace 'formatMessage' to a formatter which is used in the runtime. 28 | formatMessage = runtime.formatMessage; 29 | } 30 | } 31 | 32 | get externalPorts() { 33 | return ['A', 'B']; 34 | } 35 | 36 | get multipleExternalPorts() { 37 | return ['A', 'B', 'A+B']; 38 | } 39 | 40 | getInfo() { 41 | this.setupTranslations(formatMessage); 42 | 43 | return { 44 | id: Scratch3SpikeEssentialBlocks.EXTENSION_ID, 45 | name: 'SPIKE Essential', 46 | extensionURL: Scratch3SpikeEssentialBlocks.extensionURL, 47 | blockIconURI: blockIconURI, 48 | showStatusButton: true, 49 | blocks: this.getBlocks(formatMessage), 50 | menus: this.getMenus(formatMessage) 51 | }; 52 | } 53 | 54 | getHubTilt(args) { 55 | const value = super.getHubTilt(args); 56 | return value != null ? value / 10 : 0; 57 | } 58 | } 59 | 60 | exports.blockClass = Scratch3SpikeEssentialBlocks; 61 | module.exports = Scratch3SpikeEssentialBlocks; 62 | -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_spikeessential/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scratch-vm/src/extensions/scratch3_spikeprime/lib: -------------------------------------------------------------------------------- 1 | ../scratch3_legoble/lib -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=$(dirname $0) 4 | LF=$(printf '\\\012_') 5 | LF=${LF%_} 6 | 7 | EXTENSION_ID=$1 8 | 9 | mkdir -p node_modules/scratch-vm/src/extensions/scratch3_${EXTENSION_ID} 10 | cp -LR ${DIR}/../scratch-vm/src/extensions/scratch3_${EXTENSION_ID}/* node_modules/scratch-vm/src/extensions/scratch3_${EXTENSION_ID}/ 11 | mv node_modules/scratch-vm/src/extension-support/extension-manager.js node_modules/scratch-vm/src/extension-support/extension-manager.js_orig 12 | sed -e "s|class ExtensionManager {$|builtinExtensions['${EXTENSION_ID}'] = () => require('../extensions/scratch3_${EXTENSION_ID}');${LF}${LF}class ExtensionManager {|g" node_modules/scratch-vm/src/extension-support/extension-manager.js_orig > node_modules/scratch-vm/src/extension-support/extension-manager.js 13 | 14 | mkdir -p src/lib/libraries/extensions/${EXTENSION_ID} 15 | cp -LR ${DIR}/../scratch-gui/src/lib/libraries/extensions/${EXTENSION_ID}/* src/lib/libraries/extensions/${EXTENSION_ID}/ 16 | mv src/lib/libraries/extensions/index.jsx src/lib/libraries/extensions/index.jsx_orig 17 | sed -e "s|^export default \[$|import ${EXTENSION_ID}Entry from './${EXTENSION_ID}/index.jsx';${LF}${LF}export default [${LF} ${EXTENSION_ID}Entry,|g" src/lib/libraries/extensions/index.jsx_orig > src/lib/libraries/extensions/index.jsx 18 | -------------------------------------------------------------------------------- /scripts/register.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Register an extension in the local Scratch 3 | */ 4 | 5 | const path = require('path'); 6 | const fs = require('fs'); 7 | const { execSync } = require('child_process') 8 | 9 | function getArgs() { 10 | const args = {}; 11 | process.argv 12 | .slice(2, process.argv.length) 13 | .forEach(arg => { 14 | if (arg.slice(0, 2) === '--') { 15 | // long arg 16 | const longArg = arg.split('='); 17 | const longArgFlag = longArg[0].slice(2, longArg[0].length); 18 | const longArgValue = longArg.length > 1 ? longArg[1] : true; 19 | args[longArgFlag] = longArgValue; 20 | } 21 | else if (arg[0] === '-') { 22 | // flags 23 | const flags = arg.slice(1, arg.length).split(''); 24 | flags.forEach(flag => { 25 | args[flag] = true; 26 | }); 27 | } 28 | }); 29 | return args; 30 | } 31 | 32 | const args = getArgs(); 33 | 34 | // Make symbolic link 35 | function makeSymbolickLink(to, from) { 36 | try { 37 | const stats = fs.lstatSync(from); 38 | if (stats.isSymbolicLink()) { 39 | if (fs.readlinkSync(from) === to) { 40 | console.log(`Already exists link: ${from} -> ${fs.readlinkSync(from)}`); 41 | return; 42 | } 43 | fs.unlink(from); 44 | } else { 45 | if (process.platform === 'win32') { 46 | execSync(`rd /s /q ${from}`); 47 | } else { 48 | execSync(`rm -r ${from}`); 49 | // fs.renameSync(from, `${from}~`); 50 | } 51 | } 52 | } catch (err) { 53 | // File not esists. 54 | } 55 | fs.symlinkSync(to, from); 56 | console.log(`Make link: ${from} -> ${fs.readlinkSync(from)}`); 57 | } 58 | 59 | // Copy directory after delete old one. 60 | function copyDir(from, to) { 61 | try { 62 | const stats = fs.lstatSync(to); 63 | if (stats.isSymbolicLink()) { 64 | fs.unlinkSync(to); 65 | } else { 66 | if (process.platform === 'win32') { 67 | execSync(`rd /s /q ${to}`); 68 | } else { 69 | execSync(`rm -r ${to}`); 70 | // fs.renameSync(to, `${to}~`); 71 | } 72 | } 73 | } catch (err) { 74 | // File not esists. 75 | } 76 | if (process.platform === 'win32') { 77 | execSync(`xcopy ${from} ${to} /I /Y`); 78 | } else { 79 | execSync(`mkdir -p ${to} && cp -r ${path.join(from, '*')} ${to}`); 80 | } 81 | 82 | console.log(`copy dir ${from} -> ${to}`); 83 | } 84 | 85 | if (!args['id']) { 86 | process.stderr.write('"--id " is not set\n'); 87 | process.exit(1); 88 | } 89 | 90 | const ExtId = args['id']; 91 | 92 | const ExtDirName = args['dir'] ? 93 | args['dir'] : 94 | ExtId; 95 | 96 | const GuiRoot = args['gui'] ? 97 | path.resolve(process.cwd(), args['gui']) : 98 | path.resolve(process.cwd(), '../scratch-gui'); 99 | const VmRoot = args['vm'] ? 100 | path.resolve(process.cwd(), args['vm']) : 101 | path.resolve(GuiRoot, './node_modules/scratch-vm'); 102 | 103 | const ExtBlockPath = args['block'] ? 104 | path.resolve(process.cwd(), args['block']) : 105 | path.resolve(process.cwd(), './src/block'); 106 | 107 | const ExtEntryPath = args['entry'] ? 108 | path.resolve(process.cwd(), args['entry']) : 109 | path.resolve(process.cwd(), './src/entry'); 110 | 111 | const VmExtDirPath = path.resolve(VmRoot, `src/extensions/scratch3_${ExtDirName}`); 112 | const GuiExtDirPath = path.resolve(GuiRoot, `src/lib/libraries/extensions/${ExtDirName}`); 113 | 114 | const EntryFile = path.resolve(GuiExtDirPath, './index.jsx'); 115 | const BlockFile = path.resolve(VmExtDirPath, './index.js'); 116 | 117 | const VmExtManagerFile = path.resolve(VmRoot, './src/extension-support/extension-manager.js'); 118 | const VmVirtualMachineFile = path.resolve(VmRoot, './src/virtual-machine.js'); 119 | const GuiExtIndexFile = path.resolve(GuiRoot, './src/lib/libraries/extensions/index.jsx'); 120 | 121 | // Applay patch if it was not Xcratch 122 | if (args['base'] === 'LLK') { 123 | try { 124 | execSync(`cd ${VmRoot} && patch -p1 -N -s < ${path.resolve(__dirname, 'register/LLK/scratch-vm.patch')}`); 125 | console.log(`Apply patch: scratch-vm.patch`); 126 | execSync(`cd ${GuiRoot} && patch -p1 -N -s < ${path.resolve(__dirname, 'register/LLK/scratch-gui.patch')}`); 127 | console.log(`Apply patch: scratch-gui.patch`); 128 | } catch (err) { 129 | console.error(err); 130 | } 131 | } 132 | 133 | if (args['link']) { 134 | // Make symbolic link in scratch-vm. 135 | makeSymbolickLink(ExtBlockPath, VmExtDirPath); 136 | // Make symbolic link in scratch-gui. 137 | makeSymbolickLink(ExtEntryPath, GuiExtDirPath); 138 | } else { 139 | // Copy block dir to scratch-vm. 140 | copyDir(ExtBlockPath, VmExtDirPath); 141 | // Copy entry dir in scratch-gui. 142 | copyDir(ExtEntryPath, GuiExtDirPath); 143 | } 144 | 145 | // Replace URL in entry and block code. 146 | if (args['url']) { 147 | const url = args['url']; 148 | // Replace URL in entry 149 | let entryCode = fs.readFileSync(EntryFile, 'utf-8'); 150 | entryCode = entryCode.replace(/extensionURL:\s*[^,]+,/m, `extensionURL: '${url}',`); 151 | fs.writeFileSync(EntryFile, entryCode); 152 | console.log(`Entry: extensionURL = ${url}`); 153 | 154 | // Replace URL in entry 155 | let blockCode = fs.readFileSync(BlockFile, 'utf-8'); 156 | blockCode = blockCode.replace(/let\s+extensionURL\s+=\s+[^;]+;/m, `let extensionURL = '${url}';`); 157 | fs.writeFileSync(BlockFile, blockCode); 158 | console.log(`Block: extensionURL = ${url}`); 159 | } 160 | 161 | // Add the extension to extension manager of scratch-vm. 162 | let managerCode = fs.readFileSync(VmExtManagerFile, 'utf-8'); 163 | if (managerCode.includes(ExtId)) { 164 | console.log(`Already registered in manager: ${ExtId}`); 165 | } else { 166 | fs.copyFileSync(VmExtManagerFile, `${VmExtManagerFile}.orig`); 167 | managerCode = managerCode.replace(/builtinExtensions = {[\s\S]*?};/, `$&\n\nbuiltinExtensions.${ExtId} = () => require('../extensions/scratch3_${ExtDirName}');`); 168 | fs.writeFileSync(VmExtManagerFile, managerCode); 169 | console.log(`Registered in manager: ${ExtId}`); 170 | } 171 | 172 | if (args['C']) { 173 | // Add the extension as a core extension. 174 | let vmCode = fs.readFileSync(VmVirtualMachineFile, 'utf-8'); 175 | if (vmCode.includes(ExtId)) { 176 | console.log(`Already added as a core extension: ${ExtId}`); 177 | } else { 178 | fs.copyFileSync(VmVirtualMachineFile, `${VmVirtualMachineFile}.orig`); 179 | vmCode = vmCode.replace(/CORE_EXTENSIONS = \[[\s\S]*?\];/, `$&\n\nCORE_EXTENSIONS.push('${ExtId}');`); 180 | fs.writeFileSync(VmVirtualMachineFile, vmCode); 181 | console.log(`Add as a core extension: ${ExtId}`); 182 | } 183 | } 184 | 185 | // Add the extension to list of scratch-gui. 186 | let indexCode = fs.readFileSync(GuiExtIndexFile, 'utf-8'); 187 | if (indexCode.includes(ExtId)) { 188 | console.log(`Already added to extrnsion list: ${ExtId}`); 189 | } else { 190 | fs.copyFileSync(GuiExtIndexFile, `${GuiExtIndexFile}.orig`); 191 | const immutableDefault = /^\s*export\s+default\s+\[/m 192 | if (immutableDefault.test(indexCode)) { 193 | // Make the list of extensions mutable. 194 | indexCode = indexCode.replace(immutableDefault, 'const extensions = ['); 195 | indexCode += '\nexport default extensions;'; 196 | } 197 | indexCode += `\n// Injected for extra extension ${ExtId}`; 198 | indexCode += `\nimport ${ExtId} from './${ExtDirName}/index.jsx';`; 199 | indexCode += `\nextensions.unshift(${ExtId});`; 200 | indexCode += '\n'; 201 | fs.writeFileSync(GuiExtIndexFile, indexCode); 202 | console.log(`Added to extrnsion list: ${ExtId}`); 203 | } 204 | --------------------------------------------------------------------------------