Skip to content

Commit 4cf2c72

Browse files
author
Roy Lin
committed
feat: add transactional TSX render messages
1 parent 6ab151d commit 4cf2c72

13 files changed

Lines changed: 1675 additions & 55 deletions

README.md

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
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 architecture milestone T0 proposed" src="https://img.shields.io/badge/TSX-T0%20proposed-1687D9?style=flat-square">
9+
<img alt="TSX milestone T1 Rust foundation" src="https://img.shields.io/badge/TSX-T1%20Rust%20foundation-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

@@ -20,10 +20,9 @@ path uses a DOM, CSSOM, WebView, or framework-owned content renderer.
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
24-
> work. The TSX runtime, local host session, and npm packages are architecture
25-
> only and have not been implemented yet. React Aria component parity is not
26-
> claimed yet.
23+
> roadmap work. The TypeScript runtime, executable process host, and npm
24+
> packages have not been implemented yet; the landed Rust TSX protocol is not
25+
> a runnable TSX application. React Aria component parity is not claimed yet.
2726
2827
The target is unambiguous: A3S draws all application content. The existing
2928
`appkit-native`, `gtk4-native`, and `winui-native` modules create controls only
@@ -137,15 +136,22 @@ await createApp(Counter).run();
137136
```
138137

139138
This sample documents the target API; it is not runnable yet. The Rust-side
140-
session entrance is now executable: strict `hello`/`welcome` DTOs negotiate the
141-
renderer, capabilities, debug channels, one in-flight render, and a JSON
142-
payload limit no larger than 16 MiB; incremental and blocking codecs enforce a
143-
little-endian `u32` length prefix and reject empty, oversized, truncated,
144-
invalid UTF-8, duplicate-field, unknown-kind, and unsupported-protocol input
145-
before a session is bound. The remaining design keeps component state and callbacks in Node,
146-
keeps platform/GPU handles inside a separate Rust process, reuses resolved
147-
`ProtocolUiFrameV1` records, and sends complete frames so Rust remains the only
148-
native reconciler. Read the
139+
session boundary is now executable in Rust: strict `hello`/`welcome` DTOs
140+
negotiate the renderer, capabilities, debug channels, one in-flight render,
141+
and a JSON payload limit no larger than 16 MiB. Strict `render`, `committed`,
142+
and ordered multi-invocation `event` messages reuse resolved
143+
`ProtocolUiFrameV1` input while keeping legacy planned-widget responses out of
144+
the TSX wire contract. `TsxHostApplicationSessionV1` validates message,
145+
render, host-frame, and event sequences before mutation, preserves the active
146+
revision after a rejected frame, and maps self-drawn snapshots and dispatches
147+
directly. Incremental and blocking codecs enforce a little-endian `u32` length
148+
prefix and reject empty, oversized, truncated, invalid UTF-8, duplicate-field,
149+
unknown-kind, and unsupported-protocol input. Canonical hello, counter render,
150+
commit, and event fixtures plus a Rust RSX/TSX Native IR and accessibility
151+
parity test pin this slice. Actual process I/O, TypeScript declarations, JSX
152+
runtime, callback registry, and packages remain. The design keeps component
153+
state and callbacks in Node, platform/GPU handles inside a separate Rust
154+
process, and Rust as the only native reconciler. Read the
149155
[TSX native runtime architecture](docs/tsx-native-runtime.md) for protocol,
150156
identity, failure recovery, packaging, and T0-T5 delivery gates.
151157

@@ -355,7 +361,7 @@ independently.
355361
| 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 |
356362
| M5 · Default cutover | Planned | Make self-drawn content the default, then delete the three legacy widget renderers |
357363
| 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 |
358-
| T0-T5 · TSX native authoring | T1 Rust foundation in progress | Strict handshake, bounded length-prefixed framing, message sequencing, and revision-scoped drop-policy DTO/resolver bridge landed; render/event messages, TypeScript declarations, JSX runtime, Node callback transport, state runtime, self-drawn native window, packages, and stable SDK remain |
364+
| T0-T5 · TSX native authoring | T1 Rust foundation in progress | Strict handshake/framing, transactional render/commit/event messages, self-drawn snapshot/dispatch adapters, counter golden/parity fixtures, and revision-scoped drop-policy bridge landed; process I/O, TypeScript declarations, JSX runtime, Node callback registry/state, packages, and stable SDK remain |
359365
| 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 |
360366

361367
The dependency-ordered plan and acceptance gates are in the
@@ -499,7 +505,7 @@ src/
499505
|- rsx_app/ ComponentCx, hooks, components, and binding scope
500506
|- rsx_ui/ built-in semantic design-system registry
501507
|- protocol.rs versioned frame, event, action, ACK, and recovery boundary
502-
|- tsx_protocol/ strict handshake, message sequencing, and bounded framing
508+
|- tsx_protocol/ strict handshake/framing plus transactional render, commit, and event sessions
503509
|- native.rs portable NativeElement UI IR
504510
|- layout/ deterministic records, style projection, diffs, and tests
505511
|- drawing.rs Graphics boundary and reference/GPU renderer wrappers

docs/architecture.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -970,9 +970,16 @@ limits. `TsxHostHandshakeV1` mutates from unbound to negotiated only after the
970970
complete response fits the negotiated limit. Both stream and incremental JSON
971971
codecs validate a little-endian `u32` length before allocation, enforce the
972972
16 MiB protocol-v1 JSON-payload ceiling, and poison an incremental decoder
973-
after any framing or strict-JSON violation. This Rust transport foundation has
974-
no Node, Nub, N-API, Graphics, legacy renderer, or OS-toolkit dependency;
975-
render/event session messages and the Node implementation remain T1 work.
973+
after any framing or strict-JSON violation. Strict full-frame `render`, atomic
974+
`committed`, and ordered multi-invocation `event` messages now feed a
975+
`TsxHostApplicationSessionV1` that permits one render in flight and advances no
976+
committed state on validation or response-encoding failure. It tracks the TSX
977+
callback-scope revision separately from the self-drawn host snapshot revision,
978+
so callback-only rerenders can reuse identical Native IR safely. Feature-gated
979+
adapters consume `SelfDrawnFrameSnapshot` and `SelfDrawnInputDispatch`
980+
directly. The feature-independent foundation has no Node, Nub, N-API,
981+
Graphics, legacy renderer, or OS-toolkit dependency; commands, process I/O,
982+
generated TypeScript, and the Node implementation remain T1 work.
976983

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

docs/react-aria-native.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,11 @@ The first shared interaction milestone is available in the portable runtime:
187187
types, and disallowed operations all fail closed to `cancel`. Strict protocol
188188
v1 DTOs and `ProtocolDropPolicyResolverV1` bridge this contract without Rust
189189
evaluating JavaScript. The separate TSX boundary now also has strict atomic
190-
handshake negotiation and bounded length-prefixed JSON framing, but its
191-
render/event messages and Node callback registry/transport, external
192-
files/directories and cross-application transfer, drag previews, and full
193-
conformance evidence remain separate M7 work, so no affected family is
194-
marked conformant.
190+
handshake negotiation, bounded length-prefixed JSON framing, transactional
191+
render/commit/event messages, and a direct self-drawn dispatch adapter. Its
192+
Node callback registry/transport, external files/directories and
193+
cross-application transfer, drag previews, and full conformance evidence
194+
remain separate work, so no affected family is marked conformant.
195195

196196
- `NativeInputModality` represents keyboard, mouse, touch, pen, virtual, and
197197
unknown input.

docs/roadmap.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -623,10 +623,11 @@ Gates:
623623

624624
## P0-T TSX Native Authoring Track
625625

626-
Status: architecture accepted; the Rust-side strict handshake, bounded framing,
627-
message sequencing, and revision-scoped drop-policy protocol/resolver adapter
628-
have landed. Render/event transport messages, generated TypeScript, the JSX
629-
runtime, Node callback registry, and visible TSX application remain.
626+
Status: architecture accepted; the Rust-side strict handshake/framing,
627+
transactional render/commit/event session, self-drawn snapshot/event adapters,
628+
counter golden/parity fixtures, and revision-scoped drop-policy
629+
protocol/resolver adapter have landed. Process I/O, generated TypeScript, the
630+
JSX runtime, Node callback registry, and visible TSX application remain.
630631

631632
This track is dependency-coupled to the renderer and H0-H5 host programs
632633
without blocking Rust RSX work. Headless protocol and JSX-runtime work can
@@ -651,25 +652,29 @@ decisions are recorded in
651652
identity, and no-install-script packaging decisions
652653
- reuse the resolved `ProtocolUiFrameV1` input vocabulary behind a new TSX
653654
session envelope
654-
- pin Rust RSX and TSX counter/calculator parity fixtures
655+
- extend the landed Rust RSX/static-TSX counter parity fixture to the
656+
calculator and generated TypeScript CI
655657

656658
Gate: TSX is a peer authoring frontend and cannot bypass Native IR, layout,
657659
Graphics, interaction, accessibility, or capability checks.
658660

659661
### T1 - Headless JSX and protocol slice
660662

661-
Status: Rust transport foundation in progress. Strict `hello`/`welcome` DTOs,
662-
atomic negotiation, the fixed protocol/session/message/revision envelope,
663-
16 MiB-capped little-endian length framing, incremental decoding, and the first
664-
canonical JSON fixture have landed without Node or Graphics dependencies.
663+
Status: Rust transport foundation in progress. Strict `hello`/`welcome` plus
664+
`render`/`committed`/`event` DTOs, atomic negotiation and commit ordering, the
665+
fixed protocol/session/message/revision envelope, independent TSX and
666+
self-drawn host revisions, 16 MiB-capped little-endian framing, incremental
667+
decoding, four canonical JSON fixtures, and static counter Native
668+
IR/accessibility parity have landed. The core remains free of Node and Graphics
669+
dependencies; self-drawn conversions compile only with `platform-runtime`.
665670

666671
- generated TypeScript declarations from versioned Rust DTOs
667672
- automatic `jsx-runtime` and `jsx-dev-runtime` exports
668673
- deterministic child, prop, key, and event normalization
669-
- extend the landed bounded length-prefixed framing, handshake, and message-id
670-
sequencing with render revisions, event sequencing, process I/O, and
671-
structured diagnostics
672-
- static TSX counter rendered through the current headless semantic path
674+
- add command messages, local process I/O, and generated structured diagnostic
675+
declarations to the landed application session
676+
- extend the landed static counter semantic parity to TypeScript CI and the
677+
calculator fixture
673678

674679
Gates:
675680

docs/tsx-native-runtime.md

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# TSX to Native Runtime Architecture
22

3-
Status: proposed. No TypeScript package or TSX host is implemented yet.
3+
Status: proposed API; the Rust T1 protocol foundation is in progress. No
4+
TypeScript package or executable TSX host is implemented yet.
45

56
This document defines an optional TypeScript authoring path for A3S GUI. The
67
developer experience is a directly executable `.tsx` application:
@@ -385,9 +386,16 @@ control messages with the fixed `a3s.gui.tsx` identifier, atomic
385386
`TsxHostHandshakeV1` negotiation, exact per-sender `TsxMessageSequenceV1`, and
386387
blocking plus incremental framed JSON codecs. The decoder validates a declared
387388
length before allocating, becomes unusable after a framing/JSON violation, and
388-
has a checked end-of-stream path for partial headers and payloads. A canonical
389-
`hello-v1.json` fixture pins the current wire spelling. Render/event/command
390-
messages, actual local process I/O, and the TypeScript peer are still pending.
389+
has a checked end-of-stream path for partial headers and payloads.
390+
`TsxHostApplicationSessionV1` now accepts strict full-frame `render` messages,
391+
promotes them only through a bounded `committed` response, emits complete
392+
ordered `event` batches, permits exactly one render in flight, and leaves the
393+
active revision unchanged on validation, host, or response-encoding failure.
394+
Its feature-gated adapters project `SelfDrawnFrameSnapshot` and
395+
`SelfDrawnInputDispatch` directly; no planned-widget response is involved.
396+
Canonical hello, counter render, committed, and event fixtures pin the wire
397+
spelling. Command messages, actual local process I/O, generated TypeScript,
398+
and the TypeScript peer remain pending.
391399

392400
### Messages
393401

@@ -414,6 +422,14 @@ ordered invocation vector, applies batched state updates, and then schedules at
414422
most one new frame. Stale, skipped, or duplicated sequences are explicit
415423
session errors.
416424

425+
Protocol `renderRevision` identifies the TSX tree and callback scope.
426+
`hostRevision` identifies the committed self-drawn snapshot used for layout,
427+
hit testing, scene, and accessibility. They deliberately are not aliases: a
428+
TSX rerender may replace callbacks while producing identical Native IR, so the
429+
self-drawn host can retain its snapshot revision while the TSX render revision
430+
advances. An event must match the active host revision and is then delivered
431+
against the active TSX render revision.
432+
417433
### Commit and Recovery
418434

419435
The host performs each render as prepare, validate, commit, and present:
@@ -449,9 +465,11 @@ packages/typescript/
449465
`- tests/
450466
451467
src/tsx_protocol/
452-
|- message.rs strict control DTOs and common envelope metadata
468+
|- application.rs strict render, commit, event, context, and diagnostic DTOs
469+
|- message.rs direction-specific messages and common envelope metadata
453470
|- handshake.rs atomic capability, renderer, and limit negotiation
454471
|- framing.rs limits plus blocking and incremental JSON framing
472+
|- session.rs one-in-flight transactional revision/event ordering
455473
`- tests.rs
456474
457475
src/platform_host/ shared zero-widget host contract and OS shells
@@ -530,12 +548,13 @@ minimum M4 text/input slice.
530548

531549
### T0 - Contract and Architecture
532550

533-
Status: architecture accepted; the Rust-side strict handshake/framing boundary
534-
and drop-policy DTO/resolver adapter are implemented. The remaining parity
535-
fixtures, application message set, and Node-side transport are pending.
551+
Status: architecture accepted; the Rust-side strict handshake/framing,
552+
render/commit/event session, counter parity fixtures, self-drawn adapters, and
553+
drop-policy DTO/resolver adapter are implemented. Calculator and
554+
cross-language fixtures, commands, and Node-side transport are pending.
536555

537556
- accept process, ownership, identity, protocol, and packaging decisions
538-
- pin cross-language golden frame and event fixtures
557+
- extend the landed Rust golden frame/event fixtures into cross-language CI
539558
- define the first counter and calculator parity scenarios
540559
- record unsupported React and browser behaviors explicitly
541560

@@ -546,16 +565,19 @@ cannot bypass Native IR, layout, Graphics, interaction, or accessibility.
546565

547566
Status: Rust transport foundation in progress. `hello`/`welcome`, atomic limit
548567
and renderer negotiation, exact message-id sequencing, 16 MiB-capped framing,
549-
incremental decoding, and the first canonical JSON fixture have landed.
568+
incremental decoding, strict `render`/`committed`/`event` DTOs, transactional
569+
session ordering, self-drawn adapters, four canonical JSON fixtures, and the
570+
static counter Native IR/accessibility parity test have landed.
550571

551-
- extend the landed bounded framing and handshake DTOs with render/event/
552-
command messages and actual local process I/O
572+
- extend the landed application messages with command messages and actual
573+
local process I/O
553574
- connect the landed strict drop-policy query/response DTOs to that transport
554575
and the Node callback registry
555576
- generate TypeScript protocol declarations from Rust DTOs
556577
- publish local development exports for `jsx-runtime` and `jsx-dev-runtime`
557578
- implement element/child/prop normalization, keys, and action registration
558-
- run a static TSX frame through the existing semantic and headless pipelines
579+
- extend the landed static counter parity fixture to generated TypeScript CI
580+
and the calculator scenario
559581

560582
Gates:
561583

0 commit comments

Comments
 (0)