iris is the framework alone; the app is one crate in app-rust/
Iris: "the organization of the rust rewrite is a mess right now... there shouldn't be anything related to the app inside of iris. Iris is supposed to be the UI framework alone." And, on the crate count: "I'm confused why the app only code needs more than one crate though." Nine cargo workspaces become three, and the port's project code -- which sat in five places, four of them inside the framework -- becomes one crate, `ai-app`, in `app-rust/`: client-core -> app-rust/src/client iris/transcript-ui -> app-rust/src/ui iris/transcript-fixture -> app-rust/src/ui/fixture.rs + tests/ + touch/ iris/desktop-app -> app-rust/src/desktop + src/bin_desktop.rs iris/android-app -> app-rust/src/android + android-project/ android-shell -> app-rust/src/shell iris/ keeps core, macro, the iris crate, tabs-ui and rig-input, and now mentions no session, transcript, setup or server anywhere. Only two of the old splits had a reason that survived reading. event-model stays a crate at the repo root because server/ depends on it too, so a crate is what makes the backend and the app agree by construction. The two Android .so names looked like a hard constraint -- a package produces one library artifact -- until P2 turned out to already plan merging those two Android apps into one; both faces now come out of libai_app.so, picked apart by features so `--no-default-features --features shell` keeps wgpu, parley and iris out of the Compose app's APK. docs/RUST.md's "One app crate" has the rest, including what each remaining feature is for. DECISIONS.md and SUBAGENTS.md move into docs/ with everything else. Verified: ./run-tests.sh and `cd iris && cargo test` green, clippy and fmt clean in all five workspaces, `cargo ndk -t x86_64` links libai_app.so, build-apk.sh produces an APK that installs and launches on this checkout's emulator (Gl ... virgl, as expected), and the phone-sized headless screenshot renders the transcript unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7b54aaf3c4
commit
a9312e9431
113 files changed
+23221
-2992
No files matched your search
+39
-7
@@ -83,19 +83,51 @@ widget_trait! {
|
||||
}
|
||||
}
|
||||
|
||||
fn scrollable(self) -> impl WidgetIdFn<Rsc, Scroll> where Rsc: HasEvents {
|
||||
/// Wrap this widget in a [`ScrollArea`] that pans along `axis`, with
|
||||
/// the wheel and a finger drag both registered -- how anything with a
|
||||
/// fixed layout becomes scrollable.
|
||||
///
|
||||
/// `pin` says which end the area opens at and clings to as its content
|
||||
/// grows, and it is spelled out rather than defaulted because the two
|
||||
/// cases are not variations on each other: a composer wants the end,
|
||||
/// where what is being typed is, and a code fence opened at the end of
|
||||
/// its longest line, which is the middle of a word (seen in
|
||||
/// `iris/run-headless.sh phone`, 2026-09-08).
|
||||
///
|
||||
/// One method with the axis and the pin passed in, rather than the
|
||||
/// three named variants this used to be (Iris, 2026-09-08: "can we
|
||||
/// make both scroll methods become `.scrollable`, and it takes an axis
|
||||
/// and a pin instead of having two?"). A code fence pans across its
|
||||
/// own long lines exactly the way a transcript pans down its rows, so
|
||||
/// the two are one mechanism with the direction passed in --
|
||||
/// `DragArbiter::on` is the other half.
|
||||
///
|
||||
/// A [`LazySpan`] has an inherent `scrollable` of its own that this
|
||||
/// does not reach: it owns a controller already and must not be
|
||||
/// wrapped in an area that would slide it about as a lump.
|
||||
fn scrollable(self, axis: Axis, pin: Pin) -> impl WidgetIdFn<Rsc, ScrollArea> where Rsc: HasEvents {
|
||||
move |state| {
|
||||
Scroll::new(self.add_strong(state), Axis::Y)
|
||||
.on(CursorSense::Scroll, |ctx, rsc| {
|
||||
let delta = ctx.data.scroll_delta.y * 50.0;
|
||||
ctx.widget(rsc).scroll(delta);
|
||||
})
|
||||
.add(state)
|
||||
let area = ScrollArea::new(self.add_strong(state), axis, pin);
|
||||
scroll_senses(area, axis)(state)
|
||||
}
|
||||
}
|
||||
|
||||
fn masked(self) -> impl WidgetFn<Rsc, Masked> {
|
||||
move |state| Masked {
|
||||
shape: None,
|
||||
inner: self.add_strong(state),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clip to `shape` rather than to a plain box: `shape` is drawn
|
||||
/// behind this widget, filling the same region, and what clips is the
|
||||
/// primitive it drew -- so a rounded background and the corner its
|
||||
/// content is cut to are one rect, with no radius passed twice.
|
||||
/// Replaces `.masked().background(w)`, which drew the two but clipped
|
||||
/// to the box.
|
||||
fn masked_by<T>(self, shape: impl WidgetLike<Rsc, T>) -> impl WidgetFn<Rsc, Masked> {
|
||||
move |state| Masked {
|
||||
shape: Some(shape.add_strong(state)),
|
||||
inner: self.add_strong(state),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user