This document discusses the components used to build the editor. It's a good starting point for working on the editor or for using ideas and components from the editor in other projects.
The document assumes some familiarity with the app as a user. Try it out before reading further.
The editor is written in TypeScript using React.
The user interface is built with the Foundation's shared @microbit/ui component library, which combines react-aria-components for accessible component behaviour with Panda CSS for styling.
The project is bundled using Vite. The test runner is Vitest and we're using eslint.
We use Prettier to format code. Please set this up to format before committing changes, ideally on save (there's a VS Code plugin).
The UI is split into different areas by the workbench component. This component manages layout including expand/collapse of the sidebar and simulator and is a good starting point to navigate the codebase. The main areas are:
- The sidebar on the left, with Reference, Ideas, API and Project tabs as well as settings and help menus.
- The main editing area with the text editor for the current file, header with the project title and the project action bar with buttons for key interactions. When a micro:bit device is connected over WebUSB, the serial area appears between the text editor and the project actions.
- The simulator on the right, with its own serial area and controls.
The branding that you see on the Foundation deployment is not Open Source and is managed in a private GitHub project. This is swapped in via an alias for theme-package in the Vite config.
The device folder contains WebUSB support for the micro:bit using DAP.js. This includes an implementation of partial flashing which signficiantly speeds up subsequent flashing.
We're open to extracting this code to a separate NPM package if there's interest in reusing it. Expressions of interest and views on what should be in scope are very welcome.
To produce the correct data format to flash a micro:bit, we use the Foundation's Open Source microbit-fs library.
The Reference and Ideas sidebar tabs show educational content that is managed in the Micro:bit Educational Foundation's content management system (Sanity CMS). This content is not Open Source. The content is sourced live from the CMS APIs. It will work for local development but non-Foundation deployments will see CORS errors.
You can substitute your own content by setting VITE_SANITY_PROJECT and VITE_SANITY_DATASET environment variables, overriding the defaults in .env. The schemas we use for Sanity CMS are packaged as a plugin in this GitHub project.
We also plan to explore adding Markdown documentation support as an alternative to Sanity CMS. You can follow the discussion on #1160.
The API tab shows detailed documentation of the MicroPython API for users who need more detail than the curated content in the Reference tab provides. The API tab content is generated at runtime from the bundled type stubs for MicroPython. We do this using an enhancement to the Foundation's fork of Pyright. For more details see Python code intelligence.
We use CodeMirror 6. We selected it for its modern design, extensibility, accessibility and tablet/mobile support.
CodeMirror 6 has great documentation. The system guide is a particularly useful overview of the key concepts.
We've wrapped CodeMirror in a custom React component that configures and integrates it into our application. There are now a number of third-party React wrappers for CodeMirror that might be worth considering for other React applications.
We've written a number of non-trivial CodeMirror 6 extensions:
- A code structure highlighting extension that uses CodeMirror's syntax tree (a concrete syntax tree) to highlight the block structure of Python programs. The aim is to make Python program structure clearer. The extension assumes four space indentation and aims to guide users towards correct indentation. We'd be open to packaging this separately if there was interest.
- A custom version of CodeMirror's lint extension that delays showing red gutter markers for the actively edited line. This uses another extension that tracks the actively edited line.
- An extension that integrates with the Reference and API sidebar tabs to allow drag and drop and copy/paste of code snippets.
- A partial language server client, see Python code intelligence for more details.
Modern code intelligence features depend on a good understanding of types in the users program.
The content in the API tab, lint messages/diagnostics, autocomplete and parameter help are all provided by a fork of Microsoft's Open Source Pyright language server that runs as a Web Worker integrated with CodeMirror using a custom language server client.
We use this together with our type stubs for micro:bit MicroPython.
We've written notes on our Pyright changes in its Git repository. The micro:bit usecases are simple relative to general purpose Web-based Pyright as we have a small code size and a fixed API.
We benefit from customising Pyright and control over the language server client, allowing us to carefully consider what we present to our educational users. We intentionally have fewer features and present simplified error messages and function signatures to our users.
We don't consider our CodeMirror language server client suitable for more general use without signficant work. It's feature support is limited and it has only been tested with our Pyright fork. It also has a number of hacks specific to the micro:bit MicroPython documentation.
In addition to Pyright, we also have a custom mapping, stored in our content management system, that allows the parameter help for the MicroPython API to link to the educational content in the Reference tab. This is what powers the "Help" link you see on the parameter help tooltip.
The simulator is a separate project on GitHub. The simulator project is a Web Assembly build of MicroPython with MicroPython's hardware abstraction layer implemented using an SVG board and other Web technologies. The Python Editor communicates with it over postMessage.
If you have a micro:bit MicroPython app that would benefit from it please consider embedding the simulator. We'd be very happy to discuss this further, please raise an issue or get in touch via support.
The serial area and simulator controls below the board are part of the Python Editor project rather than the simulator. This allows embedders control over the user interface for the simulator interactions. The play action is kept inside the simulator project in part for technical reasons: iframe performance can be throttled if there is no user interaction. You may find our implementation of the controls a useful reference.
We're using xterm.js for our serial terminal, both for the device connection and the simulator. This is the same terminal component that's used in Visual Studio Code.
We've enabled the screenreader mode by default as we have less of a focus on performance and this seems suitable for an educational setting.
The editor supports a simple runtime feature flag system to:
- allow work-in-progress features to be integrated before they're ready for general use
- allow scenarios to be set up for user testing
- enable debug features
This system may change without notice. Flags are regularly added and removed.
The current set of flags are documented in the source.
Flags may be specified via the query string with repeated flag parameters,
for example, http://localhost:3000/?flag=oneFlag&flag=anotherFlag
By default, all flags are enabled for local development and branch builds.
They can be disabled with the special flag none.
We manage translations via Crowdin. Different sources of text are integrated with Crowdin in different ways.
We use react-intl from FormatJS to manage user interface strings. These are stored in this repository in lang/ui.en.json.
In development, add strings to lang/ui.en.json and run npm run i18n:compile to regenerate the catalogs the app loads in src/messages/ (gitignored; also run on install and before start, typecheck and build).
Other content:
- API documentation is managed in the type stubs repository and reaches the editor as the
@microbit/micropython-microbit-stubsnpm package (see below) - Reference and Ideas content is managed in the Foundation's content management system
- Common Pyright error messages are managed in the Foundation's Pyright fork.
The editor, the stubs repository and the Pyright fork each have an i18n.config.mjs for @microbit/i18n-tools, which downloads and uploads files one at a time via the Crowdin API rather than through a full project build and zip. download, upload and status need a Crowdin personal access token in CROWDIN_PERSONAL_TOKEN.
The lang/ui.<lang>.json files hold only what Crowdin has translated; the compile falls back to English for the rest, and npm run i18n:tidy keeps the files sorted and pruned (checked in CI).
The CMS content is managed separately by the Micro:bit Educational Foundation and is synchronized daily by an automated process.
Each of the three repositories has a translations-download workflow that runs weekly and opens a pull request; run it from the Actions tab for an earlier sync, or npm run i18n:download locally for the editor's UI strings.
The stubs and Pyright changes reach the editor differently:
- The stubs repository publishes its
typeshed.<lang>.jsonto npm as@microbit/micropython-microbit-stubswhen a GitHub release is created there. Renovate then opens a pull request here to take the new version.npm run stubs(part ofnpm run generate, run on install and before start, typecheck and build) copies the files intosrc/micropython/main/, which is gitignored. - The Pyright fork's worker is built and vendored into
public/workers/with update-pyright.sh, which expects a sibling checkout of the fork on itsmicrobitbranch.
Run the translations-upload workflow from the Actions tab when the English copy is ready to translate, or npm run i18n:upload locally. Either shows what changes before uploading (--dry-run stops there). Tick "keep translations" for a correction, such as a typo, that translators need not revisit. The stubs repository uploads the same way with npm run i18n:upload; the Pyright fork, which does not install the tool, with npx --yes --package @microbit/i18n-tools microbit-i18n upload.
This process assumes the language is already in Crowdin and has at least some translations. npm run i18n:status shows how far each language has got.
When considering a new language it's worth checking early whether support is available in lunr-languages as we need that support for our client-side search. Search is not currently supported for zh-CN and zh-TW due to the technical challenge of indexing those languages in the browser. ga-IE also lacks support in lunr-languages.
Steps:
- Add the language to the stubs repository's
i18n.config.mjs, run a sync there and cut a release. - Add the language to the editor repository:
- Add the language to
i18n.config.mjs. - Add lunr language support. See search-client.ts.
- Update
supportedLanguagesin settings.tsx. You can add the language as a preview language. In this case it will only show up on the beta deployment and will be tagged as a preview in the UI.
- Add the language to
- Add the language to the pyright repository: its
i18n.config.mjsand the switch statement inlocalize.ts. - Run a translation sync as documented above, take the stubs release and rebuild Pyright.
Follow the instructions in the issue template.
We've previously explored using the feature flag support to run a beta of MicroPython on the editor beta deployment. This might be a good strategy for feedback in future, though we'd need to coordinate it with a beta of the simulator (we already use simulator staging on beta).