├── .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 |
--------------------------------------------------------------------------------
/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 |
68 |
--------------------------------------------------------------------------------
/scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain-illustration.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/scratch-gui/src/lib/libraries/extensions/duplotrain/duplotrain-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/scratch-gui/src/lib/libraries/extensions/legoluigi/legoluigi-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/scratch-gui/src/lib/libraries/extensions/legomario/legomario-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/scratch-gui/src/lib/libraries/extensions/legopeach/legopeach-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/scratch-gui/src/lib/libraries/extensions/spikeprime/spikeprime-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------