Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/native/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,9 @@ one view, so the runtime tells the host before every in-app navigation (`navigat
history shim), and the host keeps a picture of the page it leaves. A page that leads back to the
one left (its back chevron mounts `SwipeBack`, `web-foldkit/src/platform/system-ui.ts`) slides in
over it from the right; going back slides the page off over the picture of where it goes; a drag
from the left edge does that under the finger. Tabs change at once.
to the right from anywhere on the page (as in Slack) does that under the finger, and when let go
finishes at the finger's speed, the runtime drawing where it goes while the page slides off. A
press on the tab bar never waits on a picture: the bar and the page stay live while the tab draws.

**Live data** (`HAZEL_DATA=live bun run ios`, with `VITE_APP_URL`/`VITE_BACKEND_URL` for a dev
stack on this Mac) signs in as the Mac app does: the sign-in link opens in the system's sign-in
Expand Down
21 changes: 21 additions & 0 deletions apps/native/host/ios/src/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! Lines the worker posts are the render protocol the macOS app reads from its Bun child; the
//! host's lines go back through `outbox`, each batch evaluated in the page on the main thread.
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::io::Write;
use std::sync::{Mutex, OnceLock};

Expand Down Expand Up @@ -72,6 +73,18 @@ pub fn environment(name: &str) -> Option<String> {
Some(bundled.get(name)?.as_str()?.to_string())
}

/// Whether a debug setting is on; read once.
fn environment_flag(name: &'static str) -> bool {
thread_local! {
static FLAGS: RefCell<HashMap<&'static str, bool>> = RefCell::new(HashMap::new());
}
FLAGS.with_borrow_mut(|flags| {
*flags
.entry(name)
.or_insert_with(|| environment(name).is_some())
})
}

/// Each frame's poll; the first one, with the window up, starts the runtime.
pub fn poll(s: &mut State, at: f64, tx: &mut rune::Tx) -> bool {
if !STARTED.get() {
Expand Down Expand Up @@ -166,6 +179,7 @@ fn evaluate(web: &WebView, script: &str) {

/// One line the worker posted: a tree or a request for Rune, as the macOS app handles its child's.
fn receive(line: &str) {
let started = std::time::Instant::now();
let message = match serde_json::from_str::<RuntimeMsg>(line) {
Ok(message) => message,
Err(error) => return log(&format!("[host] bad message: {error}")),
Expand All @@ -178,6 +192,13 @@ fn receive(line: &str) {
Routed::Window(message) => {
let is_tree = matches!(message, RuntimeMsg::Tree(_));
shell::apply(message, &handle);
if is_tree && environment_flag("NATIVE_STATS") {
log(&format!(
"[host] tree of {} KB parsed and applied in {:.1} ms",
line.len() / 1024,
started.elapsed().as_secs_f64() * 1000.0
));
}
// A navigation waiting on the next page moves once it is drawn.
if is_tree {
crate::system::tree_applied();
Expand Down
12 changes: 10 additions & 2 deletions apps/native/host/ios/src/islands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ use objc2_ui_kit::{
};

/// How long a tab the reader pressed stays selected before the app's own selection wins: the
/// app answers a press with a tree that selects it, or (the Menu item) one that does not.
const OPTIMISTIC: Duration = Duration::from_millis(350);
/// app answers a press with a tree that selects it, or (the Menu item) one that does not. A tree
/// that selects it ends the wait sooner, so a slow tab never flicks back to the one it left.
const OPTIMISTIC: Duration = Duration::from_millis(800);

type Rect = [f32; 4];

Expand Down Expand Up @@ -88,6 +89,8 @@ define_class!(
#[unsafe(method(tabBar:didSelectItem:))]
fn did_select(&self, _bar: &UITabBar, item: &UITabBarItem) {
self.ivars().pressed.set(Some(Instant::now()));
// Its navigation changes at once: nothing freezes the bar while the tab is drawn.
crate::system::tab_pressed();
if let Some(item) = self.ivars().items.borrow().get(item.tag() as usize) {
islands::press(item);
}
Expand Down Expand Up @@ -548,6 +551,11 @@ fn put(island: &mut Island, placement: &Placement, bottom_inset: f32) {
}
island.host.setHidden(false);
if let Native::TabBar { bar, delegate } = &island.native {
let wanted = island.spec.items.iter().position(|item| item.selected);
let shown = bar.selectedItem().map(|item| item.tag() as usize);
if wanted == shown {
delegate.ivars().pressed.set(None);
}
let settled = delegate
.ivars()
.pressed
Expand Down
Loading
Loading