Skip to content

Commit 9824de2

Browse files
Bundle simulator with extension (#15)
This is breaking change due to the change in channel name. This is required in order to use MakeCode's new third party simulator approach.
1 parent 5ce9e56 commit 9824de2

23 files changed

+4092
-1
lines changed

.github/workflows/build-simx.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Build Simulator Extension
2+
on:
3+
push:
4+
paths:
5+
- "simx/**"
6+
tags:
7+
- v**
8+
9+
workflow_dispatch:
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
matrix:
16+
node-version: [20.x]
17+
permissions:
18+
actions: read
19+
contents: write
20+
steps:
21+
- uses: actions/checkout@v4
22+
- name: Use Node.js ${{ matrix.node-version }}
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: ${{ matrix.node-version }}
26+
- run: npm ci
27+
working-directory: simx
28+
- run: npm run build
29+
working-directory: simx
30+
- name: github pages
31+
uses: peaceiris/actions-gh-pages@v3
32+
if: ${{ github.ref == 'refs/heads/master' }}
33+
with:
34+
github_token: ${{ secrets.GITHUB_TOKEN }}
35+
publish_dir: ./simx/dist
36+
allow_empty_commit: true

pxtextension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ namespace ml {
205205
data?: any;
206206
}
207207

208-
const simChannel = "microbit-ml-v1";
208+
const simChannel = "microbit-foundation/pxt-microbit-ml";
209209

210210
//% shim=TD_NOOP
211211
function simulatorRegister(): void {

simx/.eslintrc.cjs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module.exports = {
2+
root: true,
3+
env: { browser: true, es2020: true },
4+
extends: [
5+
"eslint:recommended",
6+
"plugin:@typescript-eslint/recommended",
7+
"plugin:react-hooks/recommended",
8+
],
9+
ignorePatterns: ["dist", ".eslintrc.cjs", "bin/**/*.cjs"],
10+
parser: "@typescript-eslint/parser",
11+
plugins: ["react-refresh"],
12+
rules: {
13+
"@typescript-eslint/no-explicit-any": "off",
14+
"react-refresh/only-export-components": [
15+
"warn",
16+
{ allowConstantExport: true },
17+
],
18+
},
19+
};

simx/.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
build
14+
*.local
15+
16+
# Editor directories and files
17+
.vscode/*
18+
!.vscode/extensions.json
19+
.idea
20+
.DS_Store
21+
*.suo
22+
*.ntvs*
23+
*.njsproj
24+
*.sln
25+
*.sw?

simx/LICENSE

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Copyright (c) 2024 Micro:bit Educational Foundation
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the "Software"), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7+
the Software, and to permit persons to whom the Software is furnished to do so,
8+
subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

simx/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# micro:bit Machine Learning MakeCode Simulator
2+
3+
The simulator extension for the micro:bit Machine Learning MakeCode extension.
4+
5+
This is responsible for the user interface that appears below the micro:bit board in the MakeCode simulator.
6+
7+
Bootstrapped with Vite, this app is a minimal implementation which essentially consists of a select element to allow users to simulate ML events using the iframe message protocol supported by MakeCode.
8+
9+
## Building and running the simulator
10+
11+
1. Ensure you have a working [Node.js environment](https://nodejs.org/en/download/). We recommend using the LTS version of Node and NPM.
12+
2. Install the dependencies by running `npm install` on the command line in the checkout folder.
13+
14+
### `npm run dev`
15+
16+
Runs the app in the development mode.
17+
18+
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
19+
20+
The page will reload if you make edits.
21+
22+
### `npm run build`
23+
24+
Builds the app for production to the `build` folder.
25+
26+
## Translations
27+
28+
We manage translations via Crowdin.
29+
30+
We use react-intl from [FormatJS](https://formatjs.io/) to manage user interface strings. These are stored in this repository.
31+
32+
In development, add strings to `lang/ui.en.json` and run `npm run i18n:compile` to update the strings used by the app.
33+
34+
#### Incorporting changes from Crowdin
35+
36+
Use [update-translations.sh](../bin/update-translations.sh).
37+
38+
Build and download the Crowdin zip and unzip it to a temporary location. Note the zip itself doesn't contain a top-level directory, so on Mac/Linux use e.g. `unzip -d ~/tmp/trans microbit-org.zip`. Run the script passing the directory containing the unzipped translations.
39+
40+
The script will update the UI strings.
41+
42+
#### Updating files in Crowdin
43+
44+
The UI files are updated manually. Please download the existing files and diff locally to ensure the changes are as expected.
45+
46+
### Adding a new language
47+
48+
This process assumes the language is already in Crowdin and has at least some translations.
49+
50+
Steps:
51+
52+
1. Add the language to the update script.
53+
2. Update `supportedLanguages` in [TranslationProvider.tsx](../src/messages/TranslationProvider.tsx).
54+
55+
## Deployments
56+
57+
The editor is deployed by [GitHub actions](./.github/workflows/build.yml).
58+
59+
The `main` branch is deployed to https://microbit-foundation.github.io/makecode-microbit-ml-simulator/ on each push.
60+
61+
## License
62+
63+
This software is under the MIT open source license.
64+
65+
[SPDX-License-Identifier: MIT](LICENSE)

simx/bin/tidy-lang.cjs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* Sorts the strings in the lang/*.json source files so we
3+
* don't have to care about where we add new strings.
4+
*/
5+
const fs = require("fs");
6+
const path = require("path");
7+
8+
const en = JSON.parse(fs.readFileSync("lang/ui.en.json"));
9+
const validKeys = new Set(Object.keys(en));
10+
11+
const variableRegExp = /({[a-zA-Z0-9]+})/g;
12+
13+
// This is just a best effort check that variables haven't been changed.
14+
const areTranslationsValid = (file, enJson, translatedJson) => {
15+
let valid = true;
16+
const keys = Object.keys(en);
17+
for (const k of keys) {
18+
const en = enJson[k].defaultMessage;
19+
const translated = translatedJson[k].defaultMessage;
20+
if (en.match(/, plural/)) {
21+
// Skip ICU strings as we don't understand them.
22+
continue;
23+
}
24+
const variablesEn = new Set(en.match(variableRegExp) ?? []);
25+
const variablesTranslated = new Set(translated.match(variableRegExp) ?? []);
26+
const areSetsEqual = (a, b) =>
27+
a.size === b.size && Array.from(a).every((value) => b.has(value));
28+
if (!areSetsEqual(variablesEn, variablesTranslated)) {
29+
if (valid) {
30+
console.error(file);
31+
valid = false;
32+
}
33+
console.error(` ${en}`);
34+
console.error(` ${translated}`);
35+
console.error(` Differing variables!`);
36+
console.error();
37+
}
38+
}
39+
return valid;
40+
};
41+
42+
const valid = fs
43+
.readdirSync("lang")
44+
.filter((f) => f.endsWith(".json"))
45+
.map((messages) => {
46+
const file = path.join("lang", messages);
47+
const data = {
48+
// Ensure we fallback to English even if we haven't roundtripped via Crowdin yet.
49+
...en,
50+
...JSON.parse(fs.readFileSync(file)),
51+
};
52+
Object.keys(data).forEach((k) => {
53+
if (!validKeys.has(k)) {
54+
delete data[k];
55+
}
56+
});
57+
const sortedKeys = Object.keys(data).sort();
58+
const result = Object.create(null);
59+
sortedKeys.forEach((k) => (result[k] = data[k]));
60+
fs.writeFileSync(file, JSON.stringify(result, null, 2));
61+
return areTranslationsValid(file, en, result);
62+
})
63+
.reduce((prev, curr) => prev && curr, true);
64+
process.exit(valid ? 0 : 2);

simx/bin/update-translations.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Partial automation of updating translations.
4+
#
5+
# New languages adding below and code change in settings.tsx
6+
#
7+
8+
set -euxo pipefail
9+
10+
if [ $# -eq 0 ]; then
11+
echo Missing argument to extracted Crowdin ZIP >&1
12+
exit 1
13+
fi
14+
15+
languages=""
16+
17+
mkdir -p crowdin/translated
18+
for language in $languages; do
19+
lower="${language,,}"
20+
prefix="${1}/${language}/new/apps/makecode-ml-simulator"
21+
cp "${prefix}/ui.en.json" "lang/ui.${lower}.json"
22+
done
23+
npm run i18n:compile

simx/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>micro:bit ML simulator</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

simx/lang/ui.en.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"select-label": {
3+
"defaultMessage": "ML event:",
4+
"description": "Label for the simulator event select input"
5+
}
6+
}

0 commit comments

Comments
 (0)