Skip to content

Commit a0c057a

Browse files
author
Roy Lin
committed
feat: add automatic TSX runtime core
1 parent 485a5ff commit a0c057a

25 files changed

Lines changed: 2289 additions & 46 deletions

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ jobs:
4141
node-version: "24"
4242
package-manager-cache: false
4343

44+
- name: Install TypeScript SDK development dependencies
45+
shell: bash
46+
run: npm --prefix packages/typescript ci --ignore-scripts
47+
4448
- name: Run local verification gate
4549
shell: bash
4650
run: just verify

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
/target/
2+
/packages/typescript/node_modules/

README.md

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,26 @@
66
<a href="https://github.com/A3S-Lab/GUI/actions/workflows/ci.yml"><img alt="CI status" src="https://github.com/A3S-Lab/GUI/actions/workflows/ci.yml/badge.svg"></a>
77
<img alt="Rust 1.95.0" src="https://img.shields.io/badge/Rust-1.95.0-2F3945?style=flat-square&logo=rust&logoColor=white">
88
<img alt="Roadmap milestone M3 current" src="https://img.shields.io/badge/roadmap-M3%20current-0067C0?style=flat-square">
9-
<img alt="TSX milestone T1 protocol SDK" src="https://img.shields.io/badge/TSX-T1%20protocol%20SDK-1687D9?style=flat-square">
9+
<img alt="TSX milestone T1 JSX core" src="https://img.shields.io/badge/TSX-T1%20JSX%20core-1687D9?style=flat-square">
1010
<a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-2F3945?style=flat-square"></a>
1111
</p>
1212

1313
**A3S GUI** is a Rust-native, cross-platform semantic UI and rendering runtime.
14-
Rust RSX is available today; a planned `@a3s/gui` automatic JSX runtime will
15-
let standard TSX executed by Node or Nub feed the same native pipeline. Neither
16-
path uses a DOM, CSSOM, WebView, or framework-owned content renderer.
14+
Rust RSX is available today; a private headless `@a3s/gui` automatic JSX core
15+
now lets standard TSX lower into the same versioned native frame vocabulary.
16+
Neither path uses a DOM, CSSOM, WebView, or framework-owned content renderer.
1717

1818
> [!IMPORTANT]
1919
> The repository is in its P0 renderer migration. The semantic runtime and
2020
> AppKit/GTK4/WinUI control hosts are established dogfood baselines. The first
2121
> generic self-drawn layout-to-Scene slice has landed; self-drawn text shaping,
2222
> editing/IME, accessibility bridges, and real thin-host presentation remain
23-
> roadmap work. Rust-generated TypeScript wire declarations and a private
24-
> development package now exist, but the JSX runtime, executable process host,
25-
> callback/state runtime, and publishable npm packages do not; this is not yet
26-
> a runnable TSX application. React Aria component parity is not claimed yet.
23+
> roadmap work. Rust-generated TypeScript wire declarations, automatic JSX
24+
> entry points, strict element/frame normalization, and cross-language counter
25+
> gates now exist in a private development package. The committed callback
26+
> registry, state/hooks, executable process host, and publishable npm packages
27+
> do not; this is not yet a runnable native TSX application. React Aria
28+
> component parity is not claimed yet.
2729
2830
The target is unambiguous: A3S draws all application content. The existing
2931
`appkit-native`, `gtk4-native`, and `winui-native` modules create controls only
@@ -98,7 +100,7 @@ contract.
98100

99101
## TSX to native, without a browser
100102

101-
The planned TypeScript path follows
103+
The in-progress TypeScript path follows
102104
[Nub](https://github.com/nubjs/nub)'s strongest runtime idea: keep stock Node,
103105
transform `.tsx` through its normal loader pipeline, and let a narrow Rust
104106
boundary own native work. A3S adds a standard automatic JSX runtime and a
@@ -152,9 +154,14 @@ bounded to JavaScript's exact integer range; arbitrary layout/scene
152154
fingerprints are fixed 16-digit lowercase hexadecimal strings. Rust DTOs now
153155
generate the checked-in `@a3s/gui/protocol` declarations and declaration
154156
fingerprint. Canonical hello, counter render, commit, and event fixtures are
155-
parsed and byte-canonicalized in both Rust and Node 24, while a Rust RSX/TSX
156-
Native IR and accessibility parity test pins the semantic slice. Actual process
157-
I/O, the JSX runtime, callback registry/state, and publishable packages remain.
157+
parsed and byte-canonicalized in both Rust and Node 24. The private automatic
158+
JSX runtime now expands synchronous function components, normalizes elements,
159+
children, keys, props, styles, windows, and callbacks, and produces that exact
160+
counter render fixture; pinned TypeScript 5.9 compiles the corresponding real
161+
TSX source. Rust now proves the complete compiled counter trees are equal
162+
before Native IR and accessibility parity. Actual process I/O, committed and
163+
rollback callback scopes, state/hooks, the native host, and publishable
164+
packages remain.
158165
The design keeps component state and callbacks in Node, platform/GPU handles
159166
inside a separate Rust process, and Rust as the only native reconciler. Read the
160167
[TSX native runtime architecture](docs/tsx-native-runtime.md) for protocol,
@@ -366,7 +373,7 @@ independently.
366373
| M4 · Text and interaction cutover | In progress | Stable-id raw input, long press, move, typed and collection drag/drop, timed drop activation, and fail-closed dynamic drop policy resolution landed; shaping, glyphs, editing/IME, accessibility bridges, overlays, and complete calculator scenarios remain |
367374
| M5 · Default cutover | Planned | Make self-drawn content the default, then delete the three legacy widget renderers |
368375
| H0-H5 · Thin platform hosts | H0 complete; H1 in progress | Atomic frames, lifecycle recovery, stable-id raw input/reducers, long press, captured move, typed drag/drop negotiation and timed target activation, zero-toolkit firewalls, and an interactive calculator landed; the Graphics raw-surface edge remains |
369-
| T0-T5 · TSX native authoring | T1 protocol SDK in progress | Strict handshake/framing, transactional render/commit/event messages, self-drawn adapters, safe JSON integer/fingerprint encoding, Rust-generated TypeScript declarations with drift CI, private package skeleton, counter golden/parity fixtures, and revision-scoped drop-policy bridge landed; process I/O, JSX runtime, Node callback registry/state, calculator parity, and stable SDK remain |
376+
| T0-T5 · TSX native authoring | T1 headless JSX core in progress | Strict handshake/framing, transactional render/commit/event messages, self-drawn adapters, safe JSON integer/fingerprint encoding, Rust-generated declarations, standard JSX entry points, strict element/frame normalization, TypeScript/Node golden gates, counter parity, and the drop-policy bridge landed; process I/O, committed callback scopes/state, calculator parity, native host, and stable SDK remain |
370377
| M6-M8 · React Aria components | Catalog pinned; conformance planned | 51/51 families mapped; collection DnD authoring/behavior slice landed; eight public parts, full software, accessibility, and three-OS self-drawn evidence remain |
371378

372379
The dependency-ordered plan and acceptance gates are in the

docs/architecture.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -982,10 +982,15 @@ integer limit, while arbitrary layout/scene fingerprints use lossless fixed
982982
hexadecimal strings. An optional build-only `typescript-schema` feature walks
983983
the Rust DTO graph, emits a deterministically ordered declaration module, and
984984
pins it with an FNV-1a fingerprint. A private TypeScript package imports that
985-
module in Node 24 and independently canonicalizes the Rust golden fixtures.
985+
module in Node 24, exposes standard automatic JSX entry points, normalizes
986+
immutable elements into canonical protocol frames, and independently
987+
canonicalizes the Rust golden fixtures. Pinned TypeScript 5.9 also type-checks
988+
a real `react-jsx`/`jsxImportSource` counter fixture. Function event props are
989+
replaced by deterministic action ids and retained only in a read-only
990+
per-frame callback snapshot.
986991
The feature-independent foundation has no Node, Nub, N-API, Graphics, legacy
987-
renderer, or OS-toolkit dependency; commands, process I/O, the JSX runtime,
988-
and the Node implementation remain T1 work.
992+
renderer, or OS-toolkit dependency; committed callback scopes, commands,
993+
process I/O, and the Node session implementation remain T1 work.
989994

990995
Drop policies that participate in hit testing use a separate synchronous
991996
protocol-v1 exchange rather than an action invocation. A

docs/roadmap.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -628,8 +628,11 @@ transactional render/commit/event session, self-drawn snapshot/event adapters,
628628
counter golden/parity fixtures, and revision-scoped drop-policy
629629
protocol/resolver adapter have landed. Rust-generated TypeScript declarations,
630630
a fixed schema fingerprint, a private `@a3s/gui` package skeleton, and shared
631-
Rust/Node fixture gates have also landed. Process I/O, the JSX runtime, Node
632-
callback registry, and visible TSX application remain.
631+
Rust/Node fixture gates have also landed. The automatic JSX entry points,
632+
synchronous function-component expansion, strict frame normalization,
633+
callback-to-action lowering, and real TSX type gate are now included. Process
634+
I/O, committed callback scopes, state/hooks, the native host, and a visible TSX
635+
application remain.
633636

634637
This track is dependency-coupled to the renderer and H0-H5 host programs
635638
without blocking Rust RSX work. Headless protocol and JSX-runtime work can
@@ -672,11 +675,15 @@ dependencies; self-drawn conversions compile only with `platform-runtime`.
672675
All numeric `u64` fields are bounded to JavaScript's safe integer range, full
673676
64-bit scene/layout fingerprints use fixed hexadecimal strings, and optional
674677
`typescript-schema` generation now produces the checked-in declarations and
675-
fingerprint. Node 24 independently imports that module and canonicalizes the
676-
same four fixtures without an npm dependency.
677-
678-
- automatic `jsx-runtime` and `jsx-dev-runtime` exports
679-
- deterministic child, prop, key, and event normalization
678+
fingerprint. Standard automatic JSX entry points, immutable elements,
679+
synchronous function-component expansion, strict child/key/prop/style/window
680+
normalization, deterministic callback-to-action ids, and read-only per-frame
681+
callback snapshots now lower into those declarations. Node 24 canonicalizes
682+
the same four fixtures, while pinned TypeScript 5.9 type-checks a real
683+
`react-jsx`/`jsxImportSource` counter; the package has no production dependency
684+
or install script.
685+
686+
- committed/rollback callback scopes and ordered event-vector dispatch
680687
- add command messages, local process I/O, and generated structured diagnostic
681688
declarations to the landed application session
682689
- extend the landed static counter semantic parity to the calculator fixture

docs/tsx-native-runtime.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# TSX to Native Runtime Architecture
22

3-
Status: proposed API; the T1 protocol SDK foundation is in progress. A private
4-
development package contains Rust-generated wire declarations and tests, but
5-
no JSX runtime or executable TSX host is implemented yet.
3+
Status: proposed API; the T1 headless JSX core is in progress. A private
4+
development package now contains Rust-generated wire declarations, standard
5+
automatic JSX entry points, synchronous function-component and child/key/prop
6+
normalization, deterministic frame lowering, and cross-language golden tests.
7+
The revision-scoped callback registry, state/hooks, process session, and
8+
executable native host are not implemented yet.
69

710
This document defines an optional TypeScript authoring path for A3S GUI. The
811
developer experience is a directly executable `.tsx` application:
@@ -401,9 +404,11 @@ Its feature-gated adapters project `SelfDrawnFrameSnapshot` and
401404
The optional `typescript-schema` feature walks these Rust DTOs, writes one
402405
deterministically ordered TypeScript module, and fingerprints its declaration
403406
body. Canonical hello, counter render, committed, and event fixtures pin the
404-
wire spelling in Rust and in dependency-free Node 24 tests. Command messages,
405-
actual local process I/O, the JSX runtime, and the TypeScript peer remain
406-
pending.
407+
wire spelling in Rust and Node 24 tests. The private TypeScript peer now
408+
provides standard automatic JSX entry points, synchronous function-component
409+
expansion, strict normalization, deterministic frame lowering, and a pinned
410+
TypeScript 5.9 `react-jsx` fixture. Command messages, actual local process I/O,
411+
committed callback scopes, state/hooks, and host supervision remain pending.
407412

408413
### Messages
409414

@@ -559,8 +564,9 @@ minimum M4 text/input slice.
559564
Status: architecture accepted; the Rust-side strict handshake/framing,
560565
render/commit/event session, counter parity fixtures, self-drawn adapters, and
561566
drop-policy DTO/resolver adapter are implemented. Calculator and
562-
commands, JSX execution, and Node-side transport are pending. Rust-generated
563-
declarations and shared Rust/Node counter fixtures are implemented.
567+
commands, stateful JSX execution, revision-scoped callbacks, and Node-side
568+
transport are pending. Rust-generated declarations, the headless automatic
569+
JSX core, and shared Rust/Node counter fixtures are implemented.
564570

565571
- accept process, ownership, identity, protocol, and packaging decisions
566572
- retain the landed shared Rust/Node golden frame/event gate and extend it to
@@ -580,14 +586,17 @@ session ordering, self-drawn adapters, four canonical JSON fixtures, and the
580586
static counter Native IR/accessibility parity test have landed. Safe-integer
581587
validation, hexadecimal 64-bit fingerprints, deterministic Rust-generated
582588
TypeScript declarations, a fixed schema fingerprint, the private package
583-
skeleton, and Node 24 declaration/fixture tests have also landed.
589+
skeleton, automatic `jsx-runtime`/`jsx-dev-runtime`, immutable element records,
590+
synchronous function-component expansion, strict child/key/prop/style/window
591+
normalization, deterministic callback-to-action ids, read-only callback
592+
snapshots, and Node 24 plus TypeScript 5.9 golden/type tests have also landed.
584593

585594
- extend the landed application messages with command messages and actual
586595
local process I/O
596+
- promote the landed per-frame callback snapshot into committed/rollback
597+
revision scopes and dispatch ordered event vectors against it
587598
- connect the landed strict drop-policy query/response DTOs to that transport
588599
and the Node callback registry
589-
- publish local development exports for `jsx-runtime` and `jsx-dev-runtime`
590-
- implement element/child/prop normalization, keys, and action registration
591600
- extend the landed static counter parity fixture to the calculator scenario
592601

593602
Gates:
@@ -665,7 +674,7 @@ green.
665674

666675
1. Add session DTOs, framing limits, and protocol golden fixtures. Landed.
667676
2. Add generated TypeScript wire declarations and drift CI. Landed.
668-
3. Add the automatic JSX runtime with normalization and key tests.
677+
3. Add the automatic JSX runtime with normalization and key tests. Landed.
669678
4. Add action ids, callback scopes, and event-vector dispatch.
670679
5. Add the headless host binary and a static TSX counter fixture.
671680
6. Add state/reducer/effect scheduling and counter interaction tests.

justfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ check-tsx-protocol:
309309
cargo run --locked --quiet --no-default-features --features typescript-schema --bin a3s-gui-generate-tsx-protocol -- --check
310310
cargo test --locked --no-default-features --features typescript-schema --lib tsx_protocol::typescript::
311311

312-
# Run dependency-free cross-language protocol fixture tests
312+
# Type-check automatic TSX and run dependency-free runtime/fixture tests
313313
test-typescript:
314314
npm --prefix packages/typescript test
315315

packages/typescript/README.md

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,38 @@
11
# @a3s/gui TypeScript development package
22

3-
This private package currently contains the generated protocol-v1 declarations
4-
and cross-language golden tests only. It is not a published SDK and does not yet
5-
provide `jsx-runtime`, `jsx-dev-runtime`, a Node process host, components, hooks,
6-
or a callback registry.
3+
This private package contains the first headless protocol-v1 JSX core. It now
4+
provides the standard `jsx-runtime` and `jsx-dev-runtime` entry points,
5+
immutable A3S element records, synchronous function-component expansion,
6+
fragment/child/key/prop normalization, preview `Window`/`View`/`Text`/`Button`
7+
tokens, and deterministic lowering to the Rust-generated
8+
`ProtocolUiFrameV1` declarations.
9+
10+
Function event props become collision-safe action ids. Their functions are
11+
retained in a read-only callback snapshot and never enter JSON. That snapshot
12+
is not yet the committed/rollback revision registry: event-vector dispatch,
13+
state/hooks, the Node process session, the native host executable, and the
14+
stable full semantic component API remain later delivery slices. This package
15+
is therefore not a published SDK or a runnable native TSX application yet.
16+
17+
Install the pinned development compiler without running dependency scripts:
18+
19+
```sh
20+
npm ci --ignore-scripts
21+
```
722

823
Regenerate the protocol module from the Rust DTO source of truth:
924

1025
```sh
1126
just generate-tsx-protocol
1227
```
1328

14-
Check drift and run the dependency-free Node fixture tests:
29+
Check declaration drift, compile the real automatic-runtime TSX fixture, and
30+
run the dependency-free Node runtime/golden tests:
1531

1632
```sh
1733
just check-tsx-protocol
1834
just test-typescript
1935
```
36+
37+
The TypeScript package has no production dependencies or install scripts. The
38+
pinned TypeScript compiler is a test-only development dependency.

packages/typescript/package-lock.json

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/typescript/package.json

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,20 @@
44
"private": true,
55
"description": "Development-only TypeScript protocol package for the A3S self-drawn GUI runtime",
66
"type": "module",
7-
"types": "./src/generated/protocol.ts",
7+
"types": "./src/index.ts",
88
"exports": {
9+
".": {
10+
"types": "./src/index.ts",
11+
"default": "./src/index.ts"
12+
},
13+
"./jsx-runtime": {
14+
"types": "./src/jsx-runtime.d.ts",
15+
"default": "./src/jsx-runtime.ts"
16+
},
17+
"./jsx-dev-runtime": {
18+
"types": "./src/jsx-dev-runtime.d.ts",
19+
"default": "./src/jsx-dev-runtime.ts"
20+
},
921
"./protocol": {
1022
"types": "./src/generated/protocol.ts",
1123
"default": "./src/generated/protocol.ts"
@@ -15,6 +27,11 @@
1527
"node": ">=24"
1628
},
1729
"scripts": {
18-
"test": "node --test tests/*.test.mjs"
30+
"typecheck": "tsc -p tsconfig.json",
31+
"test:runtime": "node --test tests/*.test.mjs",
32+
"test": "npm run typecheck && npm run test:runtime"
33+
},
34+
"devDependencies": {
35+
"typescript": "5.9.3"
1936
}
2037
}

0 commit comments

Comments
 (0)