Make the Rust client the sole app

This commit is contained in:
iris committed 2026-09-11 01:18:24 -04:00
1 parent a8602c1626
commit d8bb1699a8
230 files changed
+762 -27300

No files matched your search

+3 -3
View File
@@ -1,6 +1,6 @@
# `app-rust`'s `client` module
# `app`'s `client` module
`app-rust/src/client` contains platform- and UI-independent client logic. It
`app/src/client` contains platform- and UI-independent client logic. It
must not depend on iris; a `use iris::` below this directory is a layering
defect. `event-model` remains a separate crate because the server and client
both depend on that wire contract.
@@ -71,4 +71,4 @@ shared non-UI consumer needs them.
Run `./scripts/run-tests.sh` from the repository root. For this crate alone,
run `cargo test`, `cargo clippy --all-targets`, and `cargo fmt --check` from
`app-rust/`.
`app/`.
+4 -4
View File
@@ -16,16 +16,16 @@ Framework capabilities needed by `RUST.md`'s port plan:
- [ ] **Expose the distance from a `LazySpan` viewport to its unloaded
edge.** (**P1**.) `viewport_len` and the visible extents are already
measured internally, but a paging caller cannot ask whether it is within
the Compose app's six-viewport `HISTORY_SCREENS` cushion. The API should
the product's six-viewport `HISTORY_SCREENS` cushion. The API should
answer in pixels or viewport multiples, never rows: a row ranges from one
line to a screen, so a fixed row count is not a distance.
- [ ] **Let an image fit a bounded box while preserving its aspect ratio.**
(**P1**.) `Image` currently always reports and draws the decoded texture's
natural pixel size. Decoding and fetching a server-produced attachment
belong in `app-rust`; iris only owes the generic fit/scale widget used to
belong in `app`; iris only owes the generic fit/scale widget used to
draw its thumbnail.
- [ ] **Per-range backgrounds for rich text.** (**P1**.) Inline code is
already monospace and coloured, but matching Compose's chip also needs
already monospace and coloured, but the inline-code chip also needs
the glyph run's boxes so a surface can be drawn behind exactly that byte
range. The shared `TextSelection` engine already computes the same geometry
for selection highlights; expose one shared primitive rather than giving
@@ -53,7 +53,7 @@ Framework capabilities needed by `RUST.md`'s port plan:
pay nothing and import nothing for them.
- [ ] **Remove `WidgetView` unless a real composite adopts it.** Every
composite in `app-rust/src/ui` uses ordinary child handles plus a root;
composite in `app/src/ui` uses ordinary child handles plus a root;
`WidgetView` and its derive are used only by `iris/examples/view.rs`.
It currently demonstrates itself rather than shortening production code.
+11 -14
View File
@@ -6,8 +6,8 @@ need a front end at all, and owning the client means fixing what the official
app gets wrong (it won't deliver a typed message until the turn fully
finishes, where the TUI injects it at the next tool boundary).
Same shape as `../dev-updater`: a Rust (Axum) backend on the desktop, a
Kotlin/Compose Android app, pinned self-signed TLS between them.
Same shape as `../dev-updater`: a Rust/Axum backend on the desktop and a
shared Rust client for Android and desktop, with pinned self-signed TLS.
This file records decisions with their date, their rationale, and what was
rejected. Update it in place when one changes; `AGENTS.md` is the working
@@ -23,7 +23,7 @@ screens). SSH falls out of the same shape: a remote session is the identical
command wrapped in `ssh host …`, and the driver never learns which it got.
```
Android app (Compose)
Android or desktop app (Rust/iris)
│ HTTPS (pinned CA) — REST for actions, SSE for live events
backend (Rust/Axum, desktop)
@@ -702,15 +702,10 @@ ssh case are one implementation.
a hardcoded IP. The CA is created once and left alone; the leaf is reissued
every start, so covering a new address is a restart. **Regenerating the CA
strands the installed app** — the one-way door.
- Unlike dev-updater, the pinned CA is **not a constant in the source**:
the build reads `$XDG_CONFIG_HOME/ai-app/certs/ca.pem` from the machine
doing the build and generates the constant (`generatePinnedCert` in
`app/androidApp/build.gradle.kts`; `AI_APP_CA` overrides). That does
three things at once — the trust anchor follows the build machine, so an
APK built in the dev VM is only good for its emulator; there is no second
anchor to add for development and forget to remove; and regenerating a CA
needs a rebuild rather than a paste, so a stale constant cannot quietly
disagree with the server.
- The enrollment link carries the CA certificate. `app/src/client/config.rs`
decodes it and the transport pins it for every connection. A malformed or
absent CA refuses enrollment rather than silently weakening TLS. This lets
one APK enroll against either the host or an isolated development server.
- **The dev VM is untrusted** (2026-08-25): not malicious, but it could
become so. The repo is a read-write mount shared between the VM and the
backend host, so everything in it — source, binaries, and the shell scripts
@@ -797,8 +792,10 @@ ssh case are one implementation.
## App (`app/`)
Kotlin + Compose Multiplatform, single `:androidApp` module, same versions as
dev-updater (Kotlin 2.4.x, CMP 1.11.x, JDK 21).
One Rust crate owns platform-free client logic and Iris widget trees. Android
and desktop entry points contain only their host integration. Android is
packaged by a thin Java activity under `android-project/`; it does not contain
a second UI implementation.
1. **Session list** — kind icon, title, setup, model, status, last activity.
Sessions awaiting an answer sort to the top: the "your turn" inbox.
+198 -906
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -235,7 +235,7 @@ without a scroll-widget special case.
## The transcript's wiring
`app-rust/src/ui/mod.rs`, `build_tree`.
`app/src/ui/mod.rs`, `build_tree`.
The transcript registers the wheel **by hand rather than calling
`LazySpan::scrollable()`**, and this is not an oversight. That helper also