embedded-react 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -201
- package/README.md +268 -238
- package/aot/compile.mjs +2 -2
- package/aot/screenshot-smoke.mjs +110 -110
- package/assets/emit-pack.mjs +1 -1
- package/cli.mjs +147 -0
- package/package.json +12 -5
- package/persist-transform.mjs +1 -1
- package/sim/embedded-react.js +2 -0
- package/sim/embedded-react.wasm +0 -0
- package/sim/index.html +387 -0
- package/sim-server.mjs +242 -0
- package/src/embedded-react/Animated.js +357 -352
- package/src/embedded-react/AppRegistry.js +49 -49
- package/src/embedded-react/Easing.js +39 -39
- package/src/embedded-react/LayoutAnimation.js +45 -45
- package/src/embedded-react/Platform.js +26 -26
- package/src/embedded-react/StyleSheet.js +36 -36
- package/src/embedded-react/components.js +44 -44
- package/src/embedded-react/index.js +52 -52
- package/src/embedded-react/layout-anim-config.js +91 -91
- package/src/embedded-react/split-style.js +58 -58
- package/src/embedded-react/usePersistentState.js +2 -2
- package/src/host-config.js +196 -196
- package/src/native-ui.js +24 -24
- package/src/props.js +183 -183
- package/src/renderer.js +57 -57
package/README.md
CHANGED
|
@@ -1,238 +1,268 @@
|
|
|
1
|
-
# embedded-react
|
|
2
|
-
|
|
3
|
-
**React Native for embedded MCUs** — write JSX, run it on a microcontroller. This npm package is the
|
|
4
|
-
**JavaScript layer**: the React-Native-style component API you import, the
|
|
5
|
-
[`react-reconciler`](https://www.npmjs.com/package/react-reconciler) host config that drives the C engine
|
|
6
|
-
at runtime (**Flow A**), and the JSX→C ahead-of-time compiler (**Flow B**, `aot/`).
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
React → react-reconciler → host-config.js → NativeUI.* → er_scene.h (engine)
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
> ### Part of a monorepo
|
|
13
|
-
> This package is just the `bridges/quickjs/js` folder of the **embedded-react** project. The C rendering
|
|
14
|
-
> engine, the hardware backends, the runnable examples, the demo apps, and the simulator all live in the
|
|
15
|
-
> main repo — **https://github.com/TheMasterCoder007/embedded-react**. The engine itself is distributed
|
|
16
|
-
> separately as C source (CMake `FetchContent`, the ESP-IDF Component Registry, and PlatformIO) — see the
|
|
17
|
-
> repos **Install** section. Everything ships at one lockstep version (this package's version == the engine's).
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
22
|
-
> `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
import {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
container
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
- ✅ **
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
1
|
+
# embedded-react
|
|
2
|
+
|
|
3
|
+
**React Native for embedded MCUs** — write JSX, run it on a microcontroller. This npm package is the
|
|
4
|
+
**JavaScript layer**: the React-Native-style component API you import, the
|
|
5
|
+
[`react-reconciler`](https://www.npmjs.com/package/react-reconciler) host config that drives the C engine
|
|
6
|
+
at runtime (**Flow A**), and the JSX→C ahead-of-time compiler (**Flow B**, `aot/`).
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
React → react-reconciler → host-config.js → NativeUI.* → er_scene.h (engine)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
> ### Part of a monorepo
|
|
13
|
+
> This package is just the `bridges/quickjs/js` folder of the **embedded-react** project. The C rendering
|
|
14
|
+
> engine, the hardware backends, the runnable examples, the demo apps, and the simulator all live in the
|
|
15
|
+
> main repo — **https://github.com/TheMasterCoder007/embedded-react**. The engine itself is distributed
|
|
16
|
+
> separately as C source (CMake `FetchContent`, the ESP-IDF Component Registry, and PlatformIO) — see the
|
|
17
|
+
> repos **Install** section. Everything ships at one lockstep version (this package's version == the engine's).
|
|
18
|
+
>
|
|
19
|
+
> **`npx embedded-react dev` works in your own project** — it runs the WASM simulator on your app with hot
|
|
20
|
+
> reload, no clone, and no native toolchain (the simulator `.wasm` ships prebuilt in this package). The other
|
|
21
|
+
> end-to-end CLIs below (`npm run pack`/`build`/AOT, running on a board) still operate on the repo's `demos/`
|
|
22
|
+
> and `examples/`. To start a **fresh standalone project** in your own directory, use the scaffolder:
|
|
23
|
+
> `npm create embedded-react@latest my-app`.
|
|
24
|
+
|
|
25
|
+
## What an app imports
|
|
26
|
+
|
|
27
|
+
The package is the React Native analog — same idiom (hooks from `react`, everything else here):
|
|
28
|
+
|
|
29
|
+
```jsx
|
|
30
|
+
import { useState } from 'react';
|
|
31
|
+
import { View, Text, Pressable, StyleSheet, AppRegistry } from 'embedded-react';
|
|
32
|
+
|
|
33
|
+
function App() { /* ... */ }
|
|
34
|
+
AppRegistry.registerComponent('demo', () => App);
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`embedded-react` resolves as a Node **package self-reference** (`package.json` `name` + `exports`),
|
|
38
|
+
so esbuild and Vitest find it with no aliases.
|
|
39
|
+
|
|
40
|
+
## Simulate — `npx embedded-react dev`
|
|
41
|
+
|
|
42
|
+
Run your app in a browser with hot reload — no native toolchain, no repo clone. The engine is compiled to
|
|
43
|
+
WebAssembly and ships **prebuilt** in this package; the CLI bundles your JSX, serves it, and re-loads on save
|
|
44
|
+
(your `useState` survives the reload).
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx embedded-react dev # finds ./index.jsx, ./src/index.jsx, or package.json "main"
|
|
48
|
+
npx embedded-react dev app.jsx # or pass the entry explicitly
|
|
49
|
+
npx embedded-react dev --port 4000
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Open the printed URL. The canvas fills the viewport, so the browser's device toolbar drives the board size
|
|
53
|
+
(e.g., 240×320) — pixel-accurate to a hardware ARGB panel. Imported images/fonts are baked and hot-reload too.
|
|
54
|
+
A floating gear chip locks to a specific panel size and can wrap the screen in a **device frame** (bezel) for a
|
|
55
|
+
true-to-hardware preview. This is the same simulator as the repo's `tools/web-sim` (see
|
|
56
|
+
[tools/web-sim](https://github.com/TheMasterCoder007/embedded-react/blob/master/tools/web-sim/README.md)).
|
|
57
|
+
|
|
58
|
+
To **share** a UI, export a self-contained static playground — `index.html` + the prebuilt `.wasm` + your
|
|
59
|
+
bundled app — that runs in any browser with no server, ready for GitHub Pages / Netlify / a docs' iframe:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx embedded-react export --out sim-export # then: npx serve sim-export (or deploy the folder anywhere)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Layout
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
src/
|
|
69
|
+
embedded-react/ the public package surface (what apps import)
|
|
70
|
+
index.js barrel: components, StyleSheet, Platform, AppRegistry, Animated, Easing
|
|
71
|
+
components.js host component tags (View, Text, … → ERNodeType)
|
|
72
|
+
StyleSheet.js create() / flatten()
|
|
73
|
+
Platform.js { OS: 'embedded', select }
|
|
74
|
+
AppRegistry.js registerComponent(...) → mounts into a screen-sized root
|
|
75
|
+
Animated.js Value / timing / spring / decay / View|Text|Image / interpolate
|
|
76
|
+
Easing.js easing tokens (+ bezier) → engine curves
|
|
77
|
+
split-style.js pure: split style into static props + animated bindings
|
|
78
|
+
__tests__/ co-located UNIT tests for the pure surface
|
|
79
|
+
host-config.js reconciler host config → NativeUI.* (internal runtime)
|
|
80
|
+
renderer.js createRoot(props).render(...); LegacyRoot (sync) (internal)
|
|
81
|
+
props.js pure prop helpers (flattenStyle / buildProps / isEventProp)
|
|
82
|
+
native-ui.js re-exports globalThis.NativeUI (installed by the C bridge)
|
|
83
|
+
__tests__/ co-located UNIT tests (Vitest, *.unit.test.js, no engine)
|
|
84
|
+
test/runtime/ e2e tests that need the real engine host
|
|
85
|
+
*.runtime.test.jsx run inside QuickJS + engine via the headless harness
|
|
86
|
+
harness.js check()/report() — records failures for the C runner
|
|
87
|
+
run.mjs bundles each runtime test + runs er-bridge-quickjs-runtest
|
|
88
|
+
assets/ build-time asset bakers (pure JS, no native deps) — see "Assets" below
|
|
89
|
+
rasterize.mjs glyph path → coverage bitmap (supersampled, nonzero winding)
|
|
90
|
+
bake-font.mjs TTF/OTF → engine BitmapFont glyph data (opentype.js)
|
|
91
|
+
bake-image.mjs PNG → premultiplied ARGB8888 (pngjs)
|
|
92
|
+
emit-c.mjs assemble assets.generated.c + the built-in font_data.c
|
|
93
|
+
build-builtin-font.mjs regenerate the engine's default Inter font (npm run build:builtin-font)
|
|
94
|
+
build.mjs esbuild a demo's index.jsx → dist/app.bundle.js + bake its imported assets
|
|
95
|
+
pack-container.mjs bundle + bytecode-compile + bake → dist/app.erpkg config container (npm run pack)
|
|
96
|
+
assets/emit-container.mjs ERCF container writer (sections + QuickJS version stamp + CRC32)
|
|
97
|
+
vitest.config.js unit test config
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The demo apps themselves live in the repo's top-level **`demos/`** folder (one folder per demo), *not*
|
|
101
|
+
in this package — this package is the library + reconciler + AOT compiler + tests. `build.mjs` bundles a
|
|
102
|
+
selected demo and resolves its `'embedded-react'` import to `src/embedded-react/index.js`. See
|
|
103
|
+
[demos/ in the repo](https://github.com/TheMasterCoder007/embedded-react/tree/master/demos).
|
|
104
|
+
|
|
105
|
+
The host config flattens RN `style` (+ nested arrays) into the flat prop bag, routes `on*`
|
|
106
|
+
handlers to `setEvent`, and uses `shouldSetTextContent` so a flattenable `<Text>` subtree (a string,
|
|
107
|
+
interpolation like `Hi {name}`, or nested `<Text>` runs) becomes the node's `text` + inline spans.
|
|
108
|
+
`Animated`, `Easing`, and the web timer globals (`setTimeout` / `setInterval`) are all available;
|
|
109
|
+
`useEffect` flushes via the host pump.
|
|
110
|
+
|
|
111
|
+
## Build
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
npm install
|
|
115
|
+
npm run pack # default demo → dist/app.erpkg (deployable config: bytecode + assets + CRC)
|
|
116
|
+
npm run pack -- marine-dash # pack a specific demo (demos/<name>) instead
|
|
117
|
+
npm run build # lower-level: just bundle → dist/app.bundle.js (+ bake assets.generated.c)
|
|
118
|
+
npm run create -- my-app # scaffold a new app at demos/my-app (App.jsx + scripts); then `cd` + npm run sim
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`npm run pack` is the deployable artifact the desktop demo and the ESP32 both load — see **Config
|
|
122
|
+
container** below. `npm run build` is the lower-level bundle step (used by the bytecode/asset tooling
|
|
123
|
+
and by firmware that prefers to compile assets in); both bake the demo's imported images and fonts
|
|
124
|
+
(see **Assets**).
|
|
125
|
+
|
|
126
|
+
## Assets (images and fonts)
|
|
127
|
+
|
|
128
|
+
Asset handling is **import-driven** and fully build-time — there is no runtime decoder or font
|
|
129
|
+
rasterizer on the device, and no Python toolchain. An app just imports a file and uses the name it
|
|
130
|
+
returns:
|
|
131
|
+
|
|
132
|
+
```jsx
|
|
133
|
+
import logo from './assets/logo.png'; // → the baked image NAME ("logo")
|
|
134
|
+
import Inter from './assets/Inter.ttf'; // → the baked font FAMILY ("Inter")
|
|
135
|
+
|
|
136
|
+
<Image source={logo} style={{ width: 64, height: 64 }} />
|
|
137
|
+
<Text style={{ fontFamily: Inter, fontSize: 18 }}>Hi</Text>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The baker produces the assets two ways, from the same bytes: `npm run pack` packs them **into the
|
|
141
|
+
config container** (`app.erpkg`), registered at load time — this is what the desktop demo and ESP32
|
|
142
|
+
use. `npm run build` also emits `dist/assets.generated.c` exposing **`er_register_assets()`**, for
|
|
143
|
+
firmware that prefers to **compile assets into the image** and call it once at boot (`er_image_load` /
|
|
144
|
+
`er_font_register`, both flash-resident, zero runtime RAM). Either way the asset name/family is the
|
|
145
|
+
file's basename.
|
|
146
|
+
|
|
147
|
+
- **Images:** PNG → premultiplied ARGB8888 (`bake-image.mjs`, via `pngjs`).
|
|
148
|
+
- **Fonts:** TTF/OTF → pre-rasterized `BitmapFont` glyphs (`bake-font.mjs`, via `opentype.js` + a
|
|
149
|
+
pure-JS rasterizer). The engine has no runtime rasterizer, so the baker rasterizes **exactly the
|
|
150
|
+
literal `fontSize` values the bundle uses**. Computed/dynamic sizes snap to the nearest baked size
|
|
151
|
+
at runtime — pin them in `assets.config.js` if needed.
|
|
152
|
+
|
|
153
|
+
Optional per-demo overrides live in `demos/<demo>/assets.config.js`:
|
|
154
|
+
|
|
155
|
+
```js
|
|
156
|
+
export default {
|
|
157
|
+
fonts: {
|
|
158
|
+
Inter: { sizes: [14, 18, 24], bpp: 4, glyphs: 'common' }, // bpp 1|2|4|8 (4 default); glyphs: 'ascii'|'common'|'minimal'|'greek'|[codepoints]
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The engine's **built-in default font** (`engine/font/font_data.c`, the Inter fallback used by any
|
|
164
|
+
text without a custom `fontFamily`) is generated by the same baker — regenerate it with
|
|
165
|
+
`npm run build:builtin-font` (then re-run the engine text tests, as glyph metrics shift slightly).
|
|
166
|
+
|
|
167
|
+
## Config container
|
|
168
|
+
|
|
169
|
+
`npm run pack` wraps the app into one deployable file — **`dist/app.erpkg`** (format `ERCF`):
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
magic "ERCF" | format_version | crc32 | qjs_tag | sections[ bytecode, asset pack ]
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
It bundles the demo, precompiles it to **QuickJS bytecode** (no parser/source shipped), bakes the
|
|
176
|
+
imported assets into an ERPK pack, and wraps both with a **QuickJS version stamp** and an **integrity
|
|
177
|
+
CRC32**. That one `.erpkg` is "the config": loaded by `er_runtime_load_container()` on the desktop, or
|
|
178
|
+
flashed to a device's config partition. The loader verifies CRC + version (a config built for a
|
|
179
|
+
different QuickJS is rejected, not run as garbage) and registers the assets before the app mounts. This
|
|
180
|
+
is the firmware-vs-config split: the firmware (desktop exe / ESP32 image) ships once; the `.erpkg`
|
|
181
|
+
ships and updates independently.
|
|
182
|
+
|
|
183
|
+
> Two CRCs are different things: the container's internal CRC32 is embedded-react's own integrity
|
|
184
|
+
> check (universal). A bootloader's transfer/flash CRC is a separate,
|
|
185
|
+
> project-specific step layered on the `.erpkg` by your upload toolchain.
|
|
186
|
+
|
|
187
|
+
The precompiler tool must be built once (`pack` looks for it in the usual build dirs, or set
|
|
188
|
+
`ER_COMPILE_BIN`): `cmake -S bridges/quickjs -B bridges/quickjs/build && cmake --build
|
|
189
|
+
bridges/quickjs/build --target er-bridge-quickjs-compile`.
|
|
190
|
+
|
|
191
|
+
## Run (desktop)
|
|
192
|
+
|
|
193
|
+
After `npm run pack`, **rebuild the `embedded-react-desktop` target** — its build copies
|
|
194
|
+
`dist/app.erpkg` into the "config slot" next to the executable, and the host loads it **by default**
|
|
195
|
+
(no argument), exactly as the ESP32 loads its config from flash:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
examples/linux/build/embedded-react-desktop # runs the config in the slot
|
|
199
|
+
examples/linux/build/embedded-react-desktop other.erpkg # or an explicit container / .qbc / .js path
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The firmware ships no app and no baked assets — everything rides in the container. No config / a
|
|
203
|
+
corrupt one shows an on-screen panel (no built-in fallback). The C host injects the globals the app
|
|
204
|
+
expects: `NativeUI` (the bridge), `screen` (`{ width, height, scale }`), and `console`.
|
|
205
|
+
|
|
206
|
+
> Iteration loop: edit `src/*` (library) or `demos/<name>/*` (app) → `npm run pack` → rebuild
|
|
207
|
+
> `embedded-react-desktop` (re-copies the container into the slot) → run. Or, for an instant
|
|
208
|
+
> edit-save-see loop, use the **simulator** (`npm run sim`).
|
|
209
|
+
|
|
210
|
+
## Tests
|
|
211
|
+
|
|
212
|
+
Two tiers, by what they need:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
npm test # unit: Vitest over src/**/__tests__/*.unit.test.js (pure JS, no engine)
|
|
216
|
+
npm run test:runtime # e2e: bundles test/runtime/*.runtime.test.jsx, runs each in the headless
|
|
217
|
+
# QuickJS+engine harness (no window) and checks the result
|
|
218
|
+
npm run test:bytecode # same suite, but each bundle is precompiled to a .qbc bytecode blob and run
|
|
219
|
+
# via the bytecode path (JS_ReadObject) — proves the MCU load path
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The runtime tiers need the harness exe built once (no SDL); `test:bytecode` also needs the compiler:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
cmake --build bridges/quickjs/build --target er-bridge-quickjs-runtest er-bridge-quickjs-compile
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Pick the tier by what the code touches: pure marshalling/logic → a co-located `*.unit.test.js`;
|
|
229
|
+
anything that exercises the reconciler → engine pipeline → a `test/runtime/*.runtime.test.jsx`.
|
|
230
|
+
|
|
231
|
+
## Status & known gaps
|
|
232
|
+
|
|
233
|
+
- ✅ **Render, state, keyed-list reorder, and Animated all work end-to-end.** `<App/>` mounts, taps
|
|
234
|
+
re-render via `setState`, keyed reorder moves nodes (`insertBefore`/`appendChild`), and
|
|
235
|
+
`Animated.View` runs **native-driver** animations in the engine (no per-frame JS). Covered by
|
|
236
|
+
`test/runtime/reorder.runtime.test.jsx` and `animated.runtime.test.jsx`.
|
|
237
|
+
- ✅ **Timers, Promises, and `useEffect` work.** `setTimeout`/`setInterval`/`clearTimeout`/
|
|
238
|
+
`clearInterval` and the Promise job queue are serviced each frame by the host pump
|
|
239
|
+
(`er_bridge_pump`, off the engine clock). React passive effects (`useEffect`) flush on the pump.
|
|
240
|
+
Covered by `timers.runtime.test.js` and `effects.runtime.test.jsx`.
|
|
241
|
+
- ✅ **Animated composition + completion.** `sequence`/`parallel`/`stagger`/`delay`/`loop` and
|
|
242
|
+
`.start(({ finished }) => …)` all work — composition is pure JS over each child's start/stop, with
|
|
243
|
+
completion wired through the engine's `on_complete`. Covered by `anim-compose.runtime.test.js`.
|
|
244
|
+
- ✅ **Multi-child `<Text>` + nested spans.** Interpolation (`Hi {name}`) and nested styled
|
|
245
|
+
`<Text>` runs both work — a flattenable `<Text>` owns its subtree and renders as the node's text
|
|
246
|
+
plus, when runs differ in style, an inline span array (`NativeUI.setTextSpans`, max 4). Covered by
|
|
247
|
+
`text-spans` unit + runtime tests.
|
|
248
|
+
- ✅ **LayoutAnimation.** `LayoutAnimation.configureNext(...)` before a layout-changing state update
|
|
249
|
+
tweens every node whose computed rect moved on the next commit (in C — no per-frame JS). `Presets`
|
|
250
|
+
/ `create` / `Types` / `Properties` and the `easeInEaseOut`/`linear`/`spring` shorthands. Covered
|
|
251
|
+
by `layout-animation` unit + `layout-anim.runtime.test.jsx`.
|
|
252
|
+
- ✅ **Interpolate `extrapolate`.** `interpolate({ inputRange, outputRange, extrapolate })` supports
|
|
253
|
+
`'extend'` (default) / `'clamp'` / `'identity'`, with per-end `extrapolateLeft`/`extrapolateRight`
|
|
254
|
+
overrides. Math is engine-tested (`test_interpolate`); the bridge path by
|
|
255
|
+
`interpolate-extrapolate.runtime.test.js`.
|
|
256
|
+
- ✅ **Bytecode + assets + `useAnimatedValue`.** The build compiles the bundle to a `.qbc` bytecode
|
|
257
|
+
blob (the MCU load path) and bakes imported images/fonts to flash-resident C; `useAnimatedValue` is
|
|
258
|
+
exported. Runs end-to-end on the desktop host and on ESP32-S3 hardware.
|
|
259
|
+
- ✅ **State survives hot reload** — in the simulator, plain `useState` transparently persists across
|
|
260
|
+
saves (a sim-only build transform rewrites it to a persisting helper; press R to reset). On a device
|
|
261
|
+
it's just `useState`, so the same app code runs everywhere. `usePersistentState` is the underlying
|
|
262
|
+
helper, also exported for explicit use. See
|
|
263
|
+
[tools/simulator in the repo](https://github.com/TheMasterCoder007/embedded-react/blob/master/tools/simulator/README.md).
|
|
264
|
+
- ✅ **`npx embedded-react dev`** — the WASM simulator runs your app in a browser with hot reload, from your
|
|
265
|
+
own project directory, with the engine `.wasm` shipped prebuilt (no Emscripten for consumers). See above.
|
|
266
|
+
- ✅ **`npm create embedded-react@latest my-app`** — scaffolds a fresh standalone project (a styled card with a
|
|
267
|
+
pulsing logo + a `count is N` button) wired for `npm run dev` (simulator) and `npm run export` (static
|
|
268
|
+
playground). Published as the `create-embedded-react` package.
|
package/aot/compile.mjs
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
// +-*/% , comparisons, ?:)
|
|
30
30
|
//
|
|
31
31
|
// The compiler tracks which nodes depend on which state, so a state change re-sets ONLY the dependent
|
|
32
|
-
// nodes (er_node_set_props) — no diffing, no reconciler. See
|
|
32
|
+
// nodes (er_node_set_props) — no diffing, no reconciler. See the root README (Flow B).
|
|
33
33
|
//
|
|
34
34
|
// npm run aot # default demo (thermostat) — but use a minimal demo for the slice
|
|
35
35
|
// npm run aot -- music-player # a specific demo by folder name
|
|
@@ -1591,7 +1591,7 @@ function compileHandler(fnNode, env, state, out) {
|
|
|
1591
1591
|
// ---------------------------------------------------------------------------------------------------
|
|
1592
1592
|
// Emit — control flow (components / conditionals / lists) all unroll at COMPILE TIME into fixed nodes.
|
|
1593
1593
|
// Runtime-dynamic conditionals/lists (where the node COUNT changes with state) are not yet supported
|
|
1594
|
-
// and throw a clear "AOT: ..." — see
|
|
1594
|
+
// and throw a clear "AOT: ..." — see the root README (Flow B).
|
|
1595
1595
|
// ---------------------------------------------------------------------------------------------------
|
|
1596
1596
|
|
|
1597
1597
|
/** Reads a component instance's props (attributes) as static values; dynamic props throw (for now). */
|