If you want to use NinjaTerm, visit the NinjaTerm homepage to download the desktop application or use the older web-based version (the web-based version is no longer being updated).
From the homepage there are also links to the NinjaTerm manual.
You can also access older versions of NinjaTerm at GitHub Releases.
The rest of this README is for developers who want to contribute to NinjaTerm.
Below is the directory structure of this repository:
ninjaterm/
├── firmware-test-apps/ # Contains firmware test applications (Arduino sketches and a Zephyr RTT test app) used for exercising NinjaTerm against real hardware.
├── docs/ # Contains Docusaurus website which contains the homepage, installation guide and manual. Self-contained node project.
├── src/ # Contains the Electron application code.
├── tests/ # Contains the end-to-end tests using Playwright.
├── web/ # Contains the web-based version of NinjaTerm. Self-contained node project.
Clone this repo. Then run npm install to install dependencies:
npm installStart electron for development:
npm run devnpm run buildnpm run distThis will be generated in the dist directory.
Both unit tests and end-to-end tests can be run with:
npm run testUnit tests are run with vitest, which has good integration with Vite.
To run just the unit tests, use the command:
npx vitest runEnd-to-end (E2E) (a.k.a. integration tests) are performed using Playwright. The Playwright tests are located in the tests/ directory, and the Playwright config is at playwright.config.ts.
To run just the E2E tests from command line:
npx playwright testThe Playwright plug-in for VS Code is recommended if interacting with these tests, as it makes running and debugging of them easy!
Arduino sketches in firmware-test-apps/arduino_*/ allow you to program different applications onto an Arduino for testing the serial port with.
Releases are fully automated from a single command. Prerequisites on release day:
- You're on
mainwith a clean working tree, and the merge of the dev branch has landed. - The CHANGELOG's
## Unreleasedsection covers everything going out. - If the app data schema (
LATEST_VERSIONinsrc/renderer/src/model/AppDataManager/DataClasses/AppData.ts) was bumped this cycle, save a fresh default snapshot:- Run
npm run devand open the app. Settings → General → Clear app data.- DevTools Console:
copy(localStorage.getItem('appData')). - Save clipboard to
local-storage-data/appData-v{N}-app-v{X.Y.Z}-default.json.
- Run
Then cut the release. Always pass the full version explicitly — no patch /
minor / major shortcuts, just one consistent form for stable, prerelease, or
otherwise:
npm run release 5.11.0 # next minor
npm run release 5.10.1 # patch
npm run release 5.11.0-rc.1 # prerelease
npm run release v5.11.0 # v-prefix accepted too
Choosing a number follows semver:
- Major (
X.0.0) — breaking changes. Examples from the README's history: framework change, complete UI overhaul. - Minor (
5.X.0) — new features added without breaking existing ones. - Patch (
5.10.X) — bug fixes and small changes to existing functionality.
The script runs typecheck + unit tests + the snapshot preflight, finalizes
CHANGELOG (moves Unreleased into a dated section and adds the compare link),
writes package.json, commits Release v{X.Y.Z}, tags v{X.Y.Z} (annotated),
and pushes main with the tag. Pass --preview to see what the changes would
look like without writing anything, or --allow-dev to release from the dev
branch.
(We use --preview rather than --dry-run because npm intercepts --dry-run
as one of its own flags before it reaches the script.)
From there CI takes over: it builds and signs all three platforms, creates the
GitHub Release, uploads the installers / updater manifests, and extracts the
## [X.Y.Z] section from CHANGELOG.md as the Release body. There's no manual
"create draft release" step any more.
How CI works:
- On every
main/devpush:test-electronruns unit tests, the Electron e2e suite (Ubuntu), and a packaging smoke-test on all three platforms with--publish never. No upload. - On a
v*tag push:test-electronruns first, then thereleasejob rebuilds all three platforms with signing +electron-builder --publish always, which creates the GitHub Release and attaches the artifacts. A follow-uprelease-notesjob fills the Release body from CHANGELOG viagh release edit.
Netlify is used to deploy and host the static NinjaTerm HTML/JS. Netlify automatically deploys when the main branch is updated. Netlify also creates preview deploys on pull requests (link will be automatically posted into the PR comments).
NinjaTerm used to be a progressive web app (PWA). This older web app is now located in the web directory. The web directory is a project in it's own right, see it's README.md for more details.
This is no longer being updated and is in maintenance mode only.
The web app is deployed by Netlify to ninjaterm-app.mbedded.ninja.
If you get the following error in the web app:
Grid2.js:7 Uncaught TypeError: styled_default is not a function
at Grid2.js:7:26
Comment out the line:
include: ['@mui/material/Tooltip', '@emotion/styled', '@mui/material/Unstable_Grid2'],
in vite.config.ts. This should fix it. You can then uncomment the line again. Toggling this seems to fix this bug, which after reading online might be due to Vite.
Both recharts and chart.js was trialed for graphing data coming in on a serial port.
chart.js was chosen as it offered much better performance when the data update rate was fast. rechart could handle about 100 points, any more than that at the render time per new point started to take more than 50ms. chart.js can re-render 1000 points and stay under that limit.
NinjaTerm can easily handle 50kB/s of incoming serial data when maximized on a 1920x1080 screen. Reported "CPU usage" inside the app (busy time of the main javascript event loop) is around 50% when this is happening on my machine.
Make sure that often updating React components are in their own MobX observers. Prior to doing this, I had things like the throughput, CPU usage, and TX/RX activity bulbs (all in the bottom status bar) directly in the main AppView component. This causes the entire app to re-render when any of these values change, at caused noticeable performance problems at 10kB/s.
FontCreator was used to create the special NinjaTerm font. It is based of Consolas. The FontCreator project file is at src/fonts/NinjaTerm.fcp and the generated font files are NinjaTerm-Regular.woff and NinjaTerm-Regular.woff2.
The PUA (Personal Use Area) is used to add custom glyphs to represent control characters and generic hex bytes. The following code point ranges are used:
U+E000-U+E07F: Contains control character glyphs were applicable. Add0xE000to a byte which contains a control character to get the equivalent glyph.U+E100-U+E1FF: Contains a glyph for each byte from0x00to0xFFcontaining the byte as a hex number. For example,U+E100contains a glyph that says00, andU+E1FFcontains a glyph that saysFF. Add0xE100to a normal byte to get the corresponding glyph.
In FontCreator, make sure the setting Tools->Options->Fonts->Exclude unused glyphs is unchecked, otherwise glyphs at code points like 0x0001 will not be generated.
#DC3545(red): Primary colour, used for logo.#E47F37(orange): Secondary colour, used for buttons on homepage.
Settings are stored to the browser's local storage. The app data is saved under the key appData. The AppDataManager class is responsible for loading the data, updating it to the latest version if out of date, and saving it back to local storage.
The folder local-storage-data/ contains some example app data files for different versions of the app.
The files with default in the name are the default data for that app version. These are used in the unit tests to make sure upgrading app data works correctly.
- Prettier ESLint: Provides formatting of .tsx files.
- Playwright: Provides useful add-ons for running and debugging the Playwright E2E tests.
