commit before flight

This commit is contained in:
lbfalvy committed 2026-08-07 21:47:02 +00:00
1 parent 9044bc41ad
commit e5112c9cf9
15 files changed
+440 -215

No files matched your search

+4 -4
View File
@@ -1,8 +1,8 @@
[alias]
xtask = "run --quiet --package xtask --"
orcx = "xtask orcx --"
orcxdb = "xtask orcxdb --"
build-orcx = "xtask build-orcx --"
dev = "run --quiet --package dev --"
orcx = "dev orcx --"
orcxdb = "dev orcxdb --"
build-orcx = "dev build-orcx --"
[env]
CARGO_WORKSPACE_DIR = { value = "", relative = true }
Generated
+76 -73
View File
@@ -824,6 +824,8 @@ dependencies = [
"js-sys",
"orchid-api-traits",
"reqwest 0.13.4",
"scoped-log",
"unsync-pipe",
"wasm-bindgen-futures",
"web-sys",
]
@@ -1323,6 +1325,14 @@ dependencies = [
"unicode-xid 0.2.6",
]
[[package]]
name = "dev"
version = "0.1.0"
dependencies = [
"clap",
"itertools",
]
[[package]]
name = "digest"
version = "0.8.1"
@@ -1355,9 +1365,9 @@ dependencies = [
[[package]]
name = "dioxus"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c01ecf7ddbae18a419ad3d83c486101a85ffc5740ea09cdd0f09a30dc12170d"
checksum = "9320593eda4f01858f046698b42441603d2af5fcb62a6c1f9046a0c2eebd6d9b"
dependencies = [
"dioxus-asset-resolver",
"dioxus-cli-config",
@@ -1382,9 +1392,9 @@ dependencies = [
[[package]]
name = "dioxus-asset-resolver"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69387edbbc60c7cb93ad96d8cc7a22b49a76e21643380b89b1c49a78d347ff60"
checksum = "84235ff0e272f15d4537603cdd50df6d45d50e51bb96326dbc0bdfd01d825226"
dependencies = [
"dioxus-cli-config",
"http 1.4.2",
@@ -1403,18 +1413,18 @@ dependencies = [
[[package]]
name = "dioxus-cli-config"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c000f584ddf608e2b272b3074bf11512a474eeeb2eb85a1915f276ce5c4a8615"
checksum = "322d10f47effebd85ee0662a2cb083ff920d40dd6295a4afd8cfa4f9bc2e05c6"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "dioxus-config-macro"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7637091592978fbfdb45a16b26bd99fd97fb1bd7e31c6a963530e00c022af321"
checksum = "9f3a906c8219e94fa48189a793c760a4e3193cbb7791f799e9d37633fe41161d"
dependencies = [
"proc-macro2 1.0.106",
"quote 1.0.46",
@@ -1422,15 +1432,15 @@ dependencies = [
[[package]]
name = "dioxus-config-macros"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54f9ed8fc1a215ad34bb8dbae42a4ea54efbcd26ca9006bbe5cca78e511bf25f"
checksum = "20de015bc89c20f4ffc7b4f849943420308365fa66f991250b5c075261990a21"
[[package]]
name = "dioxus-core"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45887100ff0cf89abeb8b659808294fda48cd53f3b424e36407dedffcfea830b"
checksum = "1b232228ada232c0adc151e41ebd9ef70cc04683db097fa0dc6015979fa01722"
dependencies = [
"anyhow",
"const_format",
@@ -1450,9 +1460,9 @@ dependencies = [
[[package]]
name = "dioxus-core-macro"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "370c63663dff0f24df5dfea643ca239283542c6b228a302f69b32e1d36762b7f"
checksum = "6256a68462222f0600f5fd7d9542cbd9f299bc19355d900f7827dd3fc9e09fdb"
dependencies = [
"convert_case 0.8.0",
"dioxus-rsx",
@@ -1463,15 +1473,15 @@ dependencies = [
[[package]]
name = "dioxus-core-types"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36963eab106b169737762f9cd5ee5fd97f585989dcb2d8e30a596e97a6999009"
checksum = "96007ed6cbad951dfed82d83c581aabd95bc591f351f5666d454ddbe7845b324"
[[package]]
name = "dioxus-devtools"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2349cedbdf1b429df1f1bea61fdee0ad3dae7b2548eedfbeca82710122a57da0"
checksum = "434cebb282b3f820a341582ebfd37dab907d5a25c3eeafd87802468afcd2a959"
dependencies = [
"dioxus-cli-config",
"dioxus-core",
@@ -1487,9 +1497,9 @@ dependencies = [
[[package]]
name = "dioxus-devtools-types"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab9b0f7565d1916b70915f59b89ea8054ef0a9d67a364a32bbee68ef5f3818d"
checksum = "dfd6f3475e38c93be245a664a9ce11679a8f2b99832620705cf7837f42aa39ce"
dependencies = [
"dioxus-core",
"serde",
@@ -1498,9 +1508,9 @@ dependencies = [
[[package]]
name = "dioxus-document"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e3a5bec7ffc999ff23446a487eb5cd86111d1574a23533dd3f8b3c69a53a22"
checksum = "2f6fadd5c6d886b48b762820abae1a08142a2159f8a1136c0466b4b4ed9f547e"
dependencies = [
"dioxus-core",
"dioxus-core-macro",
@@ -1517,9 +1527,9 @@ dependencies = [
[[package]]
name = "dioxus-fullstack"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37f0558edb88af5ad47275ae36a7f06317163ba482db377c26d7d8590b5cd0f6"
checksum = "a91d5acabd1470145bcf164cab9fd2edcf2e4fba0fa6fb0fd6dca23079fcc169"
dependencies = [
"anyhow",
"async-stream",
@@ -1574,9 +1584,9 @@ dependencies = [
[[package]]
name = "dioxus-fullstack-core"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc634b28b4b1e3eab1e8df4f98510e2d2fa39d686321467f977213155e86ed2b"
checksum = "0011e9bca8da2ae6bf02ba9ef93ba6c5fa539e402de3aeb316ad86ca77c59079"
dependencies = [
"anyhow",
"axum-core",
@@ -1602,9 +1612,9 @@ dependencies = [
[[package]]
name = "dioxus-fullstack-macro"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85a8fe7da549859fae00c7f4bf11a2aab734ae7ef6f98f280dce9bea1f3326ec"
checksum = "681f39665de36af7bb724b1cc0ca9c2b7c36a2b93febf1b2911716479eac3db0"
dependencies = [
"const_format",
"convert_case 0.8.0",
@@ -1616,9 +1626,9 @@ dependencies = [
[[package]]
name = "dioxus-history"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a15232302d1933015fcf2d6fe9e286ad36f6e9c205a546089a0f326023bb0d2"
checksum = "2404b77d4441e694a5e93afb5c9a729efb26a3f6920f769cfe6cfedf5c7ac210"
dependencies = [
"dioxus-core",
"tracing",
@@ -1626,9 +1636,9 @@ dependencies = [
[[package]]
name = "dioxus-hooks"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4534f91cf6305204b948bdec130076ac9ecc7c22faab29475b76870558bf73ea"
checksum = "9d3807a1bc039299cd37ad85f1c56ae7088210e8a38e3e877f01034d68c15fbb"
dependencies = [
"dioxus-core",
"dioxus-signals",
@@ -1642,9 +1652,9 @@ dependencies = [
[[package]]
name = "dioxus-html"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e03d6ad4040b667f2b2eefcb678840e630938c09bf9ec39b04ea4d1d96d90d44"
checksum = "40b9a85679c4dfd8699407a9ad5041a6a4fe6be0ee386353951cd03b57a12030"
dependencies = [
"async-trait",
"bytes 1.12.1",
@@ -1666,9 +1676,9 @@ dependencies = [
[[package]]
name = "dioxus-html-internal-macro"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "584e2772127ab00f0d5e1d4d9795f39fecebc828ece0b7a02349d438bc1b1ce7"
checksum = "500282dad5b62e91f93e127cbf41c587bf1c457d52514a75f3ff2a55fd0ea9f6"
dependencies = [
"convert_case 0.8.0",
"proc-macro2 1.0.106",
@@ -1678,9 +1688,9 @@ dependencies = [
[[package]]
name = "dioxus-interpreter-js"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11999d6eb5bb179a9512dad30e5de408aab66f2cb65de9098c9fbe02927e2978"
checksum = "faa22cc3431e7efdbae28069542a1763c977d5edbe4d4fbfb0b7667b9c9966e9"
dependencies = [
"js-sys",
"lazy-js-bundle",
@@ -1694,9 +1704,9 @@ dependencies = [
[[package]]
name = "dioxus-logger"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ccdfe36d2cb830a2784e40f7e6f7199805a2c6da99bd65b1ca308f11aed28"
checksum = "0f36b2dac4df8c747431f77f2a6644dd033ea98e7f8ac80271ff47fd4ba8a548"
dependencies = [
"dioxus-cli-config",
"tracing",
@@ -1706,9 +1716,9 @@ dependencies = [
[[package]]
name = "dioxus-rsx"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2106afda239a4c7c22ffa1ca19117011225fc1c735c139c0a5b765996aa8bb1d"
checksum = "aea662c9e13a91279d8acbc0d02a80430c7081704c20e8c791e4c4a151268596"
dependencies = [
"proc-macro2 1.0.106",
"proc-macro2-diagnostics",
@@ -1719,9 +1729,9 @@ dependencies = [
[[package]]
name = "dioxus-signals"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3705754f5e043deec9fc7af0d159f18e5b21c02c47d255c7e477f31368f0b6d2"
checksum = "95d85e36fcaf7abdc986836801bf248776da7d5961f5fac65fdbf5ed491afb4f"
dependencies = [
"dioxus-core",
"futures-channel",
@@ -1735,9 +1745,9 @@ dependencies = [
[[package]]
name = "dioxus-stores"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64bec7b21c86b1360ec965a07a53a2c96b7caee3465049e1c299a45024e87614"
checksum = "15eaa9f2ccc5ce962d056515e61345c846a54b13a2b880cf7e8ac93faa53a178"
dependencies = [
"dioxus-core",
"dioxus-signals",
@@ -1747,9 +1757,9 @@ dependencies = [
[[package]]
name = "dioxus-stores-macro"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40a5875e9f890f27b1cc3e5b56c1e23601211470315a1fb8627c4ca4f3b2be9a"
checksum = "a13bd8a693d7042544fa3ac79698a08783bbc698d1ed257e88c5d4059e9ac476"
dependencies = [
"convert_case 0.8.0",
"proc-macro2 1.0.106",
@@ -1759,9 +1769,9 @@ dependencies = [
[[package]]
name = "dioxus-web"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0a0be76b404e8242a597db0fb239d05f8dee4e7856bc1fc7144f7e244822fd"
checksum = "481093233d789c5ffbdde66e7970e803b01a199a7ab0c0f8ef84c65d30b7384f"
dependencies = [
"dioxus-cli-config",
"dioxus-core",
@@ -2235,9 +2245,9 @@ dependencies = [
[[package]]
name = "generational-box"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd0d825b8d339701ad330dbcd6399519ced4d143484954daf6e3185dace4f77"
checksum = "b8b2dc9b873cd1a8adb80aa5dcef9a8205f781a8a313d5ffb867248cb3bcb764"
dependencies = [
"parking_lot",
"tracing",
@@ -3080,9 +3090,9 @@ checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy-js-bundle"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccafada6c9541db44db758619236f2748f6e1bdaa84d04ded858567cd1e89321"
checksum = "51cdaa5abc885e2d606e6985281ace220778bde190be20aab1ec2346ed8bd1fa"
[[package]]
name = "lazy_static"
@@ -3238,9 +3248,9 @@ dependencies = [
[[package]]
name = "manganis"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bfcf56309de35b48b8780ea097ace5c3b773a617b52edc49dfc9a63a7d9dc43"
checksum = "1ed2af894a724a0b6e521f9efe84f98cfdc74a4de6828efef77ffd3b1679fd52"
dependencies = [
"const-serialize 0.7.2",
"const-serialize 0.8.0-alpha.0",
@@ -3254,9 +3264,9 @@ dependencies = [
[[package]]
name = "manganis-core"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a24d6be68f594495aea60850a284029d585d7b7839b26096c1b6d758f8518648"
checksum = "e258c71b137bf48deaffb48b8a8a2f994af9e5c3ca201daa20d41e26b379da11"
dependencies = [
"const-serialize 0.7.2",
"const-serialize 0.8.0-alpha.0",
@@ -3268,9 +3278,9 @@ dependencies = [
[[package]]
name = "manganis-macro"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e782a10318d707c0833e31876ded8acf91287eee0010af8392559af614c7226"
checksum = "0abae8346bbf637e6d21ac3831863bd5a98657517f5cd2d69d651046b95e7ce9"
dependencies = [
"dunce",
"macro-string",
@@ -5109,9 +5119,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subsecond"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cc79674bd55726e6b123204403389400229a95fe4a3b2c5453dada70b06ca95"
checksum = "d350d5788fa94d560d92269266a50efc77b036bfca6675e420b64df7b3211f37"
dependencies = [
"js-sys",
"libc",
@@ -5128,9 +5138,9 @@ dependencies = [
[[package]]
name = "subsecond-types"
version = "0.7.9"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9798bfed58797aed51c672aa99810aac30a50d3120ecfdcf28c13784e9a8f1c"
checksum = "dcf32d66269b5fbb8558334e8d22b657b2f7af0dd2ef56c231541099a862e464"
dependencies = [
"serde",
]
@@ -5240,10 +5250,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.3.4",
"getrandom 0.4.3",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.61.2",
]
[[package]]
@@ -6462,13 +6472,6 @@ dependencies = [
"tap",
]
[[package]]
name = "xtask"
version = "0.1.0"
dependencies = [
"clap",
]
[[package]]
name = "xxhash-rust"
version = "0.8.16"
+1 -1
View File
@@ -12,7 +12,7 @@ members = [
"orchid-api-traits",
"scoped-value",
"stdio-perftest",
"xtask",
"dev",
"async-fn-stream",
"unsync-pipe",
"orchid-async-utils",
+46 -33
View File
@@ -7,11 +7,11 @@ use std::sync::Arc;
use actix_cors::Cors;
use actix_web::middleware::Logger;
use actix_web::{App, Error, HttpRequest, HttpResponse, HttpServer, Responder, get, post, rt, web};
use actix_ws::{AggregatedMessage, ProtocolError};
use actix_ws::{AggregatedMessage, CloseCode, ProtocolError};
use async_event::Event;
use futures::lock::Mutex;
use futures::stream::StreamExt as _;
use futures::stream_select;
use futures::{FutureExt, stream_select};
#[derive(Clone)]
struct Message {
@@ -60,15 +60,10 @@ async fn read(
let data_changes =
pin!(futures::stream::unfold((topic.clone(), 0usize), async |(topic, cursor)| {
eprintln!("Listening for new data");
let len_chg = (topic.pushed)
.wait_until(|| {
eprintln!("Checking for new data");
NonZero::new(topic.backlog.lock().unwrap().len() as i64 - cursor as i64)
})
.wait_until(|| NonZero::new(topic.backlog.lock().unwrap().len() as i64 - cursor as i64))
.await;
if let Ok(added) = usize::try_from(len_chg.get()) {
eprintln!("Found new data");
let next_cursor = cursor + added;
Some((
SubscrEvent::Ingress(Range { start: cursor, end: next_cursor }),
@@ -81,44 +76,62 @@ async fn read(
}));
let mut events = stream_select!(stream, data_changes);
let mut client_cursor = 0;
while let Some(ev) = events.next().await {
eprintln!("activity!");
fn proto_error(msg: &str) -> actix_ws::CloseReason {
actix_ws::CloseReason {
code: actix_ws::CloseCode::Protocol,
description: Some(msg.to_string()),
}
}
let close_reason = 'sock_loop: loop {
let Some(ev) = events.next().await else { break 'sock_loop None };
match ev {
SubscrEvent::Message(Err(e)) => {
eprintln!("{e}");
session.close(None).await.unwrap();
break;
break 'sock_loop None;
},
SubscrEvent::Message(Ok(AggregatedMessage::Ping(msg))) => session.pong(&msg).await.unwrap(),
SubscrEvent::Message(Ok(AggregatedMessage::Pong(_))) => (),
SubscrEvent::Message(Ok(AggregatedMessage::Text(msg))) if msg.trim() == "exit" => {
session
.close(Some(actix_ws::CloseReason {
code: actix_ws::CloseCode::Normal,
description: Some("User requested shutdown by sending \"exit\"".to_string()),
}))
.await
.unwrap();
break;
},
SubscrEvent::Message(Ok(AggregatedMessage::Close(Some(reason)))) => eprintln!("{reason:?}"),
SubscrEvent::Message(Ok(AggregatedMessage::Close(_))) => (),
SubscrEvent::Message(Ok(AggregatedMessage::Text(tx))) => {
publish_on_topic(&topic, Message { data: tx.into_bytes() }).await;
let msg = tx.trim();
if msg == "exit" {
break Some(actix_ws::CloseReason {
code: actix_ws::CloseCode::Normal,
description: Some("User requested shutdown by sending \"exit\"".to_string()),
});
} else if let Some(new_cursor) = msg.strip_prefix("cursor=") {
let Ok(new_cursor) = new_cursor.parse::<usize>() else {
break 'sock_loop Some(proto_error("Could not parse cursor offset"));
};
client_cursor = new_cursor;
} else {
publish_on_topic(&topic, Message { data: tx.into_bytes() }).await
}
},
SubscrEvent::Message(Ok(AggregatedMessage::Binary(data))) => {
publish_on_topic(&topic, Message { data }).await;
},
SubscrEvent::Reset => {
session.text("reset").await.unwrap();
},
SubscrEvent::Ingress(range) => {
let payload = topic.backlog.lock().unwrap()[range].to_vec();
for msg in payload {
session.binary(msg.data).await.unwrap();
SubscrEvent::Message(Ok(AggregatedMessage::Binary(data))) =>
publish_on_topic(&topic, Message { data }).await,
SubscrEvent::Reset => session.text("reset").await.unwrap(),
SubscrEvent::Ingress(mut range) => {
range.start = range.start.max(client_cursor);
let payload = (topic.backlog.lock().unwrap()[range].iter())
.flat_map(|msg| msg.data.iter().copied())
.collect::<Vec<_>>();
for chunk in payload.chunks(8192) {
if let Err(e) = session.binary(chunk.to_vec()).await {
eprintln!("Error on receiving binary: {e}");
break 'sock_loop None;
}
}
},
}
};
if let Some(close) = close_reason {
session.close(Some(close)).await.unwrap();
}
});
+3 -1
View File
@@ -5,7 +5,7 @@ edition = "2024"
[dependencies]
checklings = { version = "0.1.0", path = "../checklings" }
dioxus = { version = "0.7.9", features = ["web"] }
dioxus = { version = "0.7.10", features = ["web"] }
facet-xdr = "0.46.1"
futures = "0.3.32"
gloo-net = "0.7.0"
@@ -13,6 +13,8 @@ itertools = "0.15.0"
js-sys = "0.3.103"
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
reqwest = { version = "0.13.4", features = ["json"] }
scoped-log = { version = "0.1.0", path = "../scoped-log" }
unsync-pipe = { version = "0.2.0", path = "../unsync-pipe" }
wasm-bindgen-futures = "0.4.76"
web-sys = { version = "0.3.103", features = [
"Storage",
+31 -4
View File
@@ -1,6 +1,33 @@
body {
background-color: #0f1116;
color: #ffffff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 20px;
background-color: #0f1116;
color: #ffffff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
--bg-btn: #444;
--bg-btn-emphasis: #55a;
--input-under: #444;
--input-under-focus: #55a;
}
input {
color: #ffffff;
padding: 5px 10px;
margin: 5px;
border: 0px;
border-bottom: 2px solid var(--input-under);
background-color: transparent;
&:focus {
border-bottom: 2px solid var(--input-under-focus);
outline: none;
}
}
button {
padding: 5px 10px;
margin: 5px;
border: 0;
color: white;
background: var(--bg-btn);
}
+166 -43
View File
@@ -1,8 +1,11 @@
use std::pin::pin;
use std::time::Duration;
use checklings::{TraceInfo, TracedEvent};
use dioxus::prelude::*;
use dioxus::web::WebEventExt;
use futures::AsyncWriteExt;
use futures::future::join;
use futures::stream::StreamExt;
use gloo_net::websocket::Message;
use gloo_net::websocket::futures::WebSocket;
@@ -10,7 +13,9 @@ use js_sys::wasm_bindgen::JsCast;
use orchid_api_traits::Decode;
mod web_bindings;
mod virtual_scroll;
use scoped_log::{NullLogger, with_logger};
use web_bindings::*;
const MAIN_CSS: Asset = asset!("/assets/main.css");
@@ -43,25 +48,48 @@ fn App() -> Element {
let mut log = use_signal(Vec::<Result<TracedEvent, String>>::new);
use_resource(move || async move {
'connect: loop {
loop {
writeln!(Console, "Initiating websocket");
let url = format!("{}/topic/{}/read", remote(), topic());
let Ok(mut socket) = WebSocket::open(&url) else {
log.set(vec![Err(format!("Failed to construct websocket at {url}"))]);
break 'connect;
break;
};
log.set(Vec::new());
while let Some(msg) = socket.next().await {
match msg {
Err(e) => {
log.write().push(Err(format!("connection error: {e}")));
sleep(Duration::from_secs(3)).await;
continue 'connect;
},
Ok(Message::Text(txt)) if txt == "reset" => log.set(Vec::new()),
Ok(Message::Text(txt)) => log.write().push(Err(format!("text: {txt}"))),
Ok(Message::Bytes(bytes)) =>
log.write().push(Ok(TracedEvent::decode_slice(&mut &bytes[..]))),
}
writeln!(Console, "Websocket ready, waiting for messages");
let (mut writer, reader) = unsync_pipe::pipe(1024);
let (retry, ()) = join(
with_logger(NullLogger, async move {
while let Some(msg) = socket.next().await {
match msg {
Err(e) => {
writeln!(Console, "Failed to connect websocket, {e}");
log.write().push(Err(format!("connection error: {e}")));
sleep(Duration::from_secs(3)).await;
return true;
},
Ok(Message::Text(txt)) if txt == "reset" => log.set(Vec::new()),
Ok(Message::Text(txt)) => log.write().push(Err(format!("text: {txt}"))),
Ok(Message::Bytes(bytes)) =>
if let Err(e) = writer.write_all(&bytes).await {
writeln!(Console, "{e}");
},
}
}
false
}),
with_logger(NullLogger, async move {
let mut source = pin!(reader);
loop {
let event = (TracedEvent::decode(source.as_mut()).await.map_err(|e| e.to_string()))
.inspect_err(|e| writeln!(Console, "{e}"));
log.write().push(event);
}
}),
)
.await;
if !retry {
break;
}
}
});
@@ -69,53 +97,148 @@ fn App() -> Element {
rsx! {
// Global app resources
document::Link { rel: "stylesheet", href: MAIN_CSS }
input {
placeholder: "remote",
value: remote(),
oninput: move |event| {
remote.set(event.value());
div {
class: "app",
height: "100vh",
width: "100vw",
display: "grid",
grid_template: "
\"menu \" auto
\"msg_log\" 1fr
/ auto
",
div {
grid_area: "menu",
input {
placeholder: "remote",
min_width: "200px",
value: remote(),
oninput: move |event| {
remote.set(event.value());
}
}
input {
placeholder: "topic",
value: topic(),
oninput: move |event| {
topic.set(event.value());
}
}
}
}
input {
placeholder: "topic",
value: topic(),
oninput: move |event| {
topic.set(event.value());
div {
grid_area: "msg_log",
overflow_y: "hidden",
Log {
rows: log
}
}
}
Log {
rows: log
}
}
}
#[component]
fn Log(rows: ReadSignal<Vec<Result<TracedEvent, String>>>) -> Element {
let mut at_bottom = use_signal(|| true);
let mut scroll_area = use_signal(|| None);
let mut follow_once = use_signal(|| true);
let mut new_bot_idx = use_signal(|| None);
let mut new_top_idx = use_signal(|| None);
use_effect(move || {
let _rerun_trigger = rows.read().len();
if let Some(scroll) = &*scroll_area.read()
&& at_bottom()
&& *follow_once.read()
&& !is_at_bottom(scroll, 2)
{
scroll_to_bottom(scroll);
scroll_to_bottom(scroll, web_sys::ScrollBehavior::Instant);
follow_once.set(false);
}
});
let rows_g = rows.read();
const INITIAL_VIEW_SIZE: usize = 1000;
const OVERSCROLL_SIZE: usize = 10;
const LOAD_BATCH_SIZE: usize = 200;
// The log never un-loads a row that has been manually loaded
// bottom index either
// - extended via overscroll
// - implied by a specific top idx
// - assumed to be the end of the log
let bot_idx =
new_bot_idx().unwrap_or(new_top_idx().map_or(rows_g.len(), |b| b + INITIAL_VIEW_SIZE));
// top index either
// - extended via overscroll
// - offset from the end of the log
let top_idx = new_top_idx().unwrap_or(bot_idx.saturating_sub(INITIAL_VIEW_SIZE));
// overscroll areas cut from the ends of the span and cannot overlap
let midpoint = (top_idx + bot_idx) / 2;
// overscroll region info
// whether it's needed (whether there is anything to show)
let top_oscr_visible = 0 < top_idx;
let bot_oscr_visible = bot_idx < rows_g.len();
// where it ends into the sequence
let top_oscr_end = if top_oscr_visible { midpoint.min(top_idx + OVERSCROLL_SIZE) } else { 0 };
let bot_oscr_start = if bot_oscr_visible {
midpoint.max(bot_idx.saturating_sub(OVERSCROLL_SIZE))
} else {
rows_g.len()
};
// to what point it should extend the loaded region
let next_top_idx = top_idx.saturating_sub(LOAD_BATCH_SIZE);
let next_bot_idx = rows_g.len().max(bot_idx + LOAD_BATCH_SIZE);
// split the sequence into actual overscroll regions
let top_oscr = &rows_g[top_idx..top_oscr_end];
let mid_section = &rows_g[top_oscr_end..bot_oscr_start];
let bot_oscr = &rows_g[bot_oscr_start..bot_idx];
// render callback not cached
let render_line = |line: &Result<TracedEvent, String>| -> Element {
match line {
Err(msg) => rsx! { TextLine { msg: msg } },
Ok(event) => rsx! { EventLine { event: event.clone() }},
}
};
rsx! {
div {
overflow_y: "scroll",
onmounted: move |ev| {
*scroll_area.write() = Some(ev.data());
},
onscroll: move |ev| {
if let Some(scroll) = scroll_area.read().as_ref() {
*at_bottom.write() = is_at_bottom(scroll, 0);
class: "log",
min_height: "5em",
height: "100%",
position: "relative",
button {
position: "absolute",
bottom: "10px",
right: "10px",
onclick: move |_| follow_once.toggle(),
background_color: if follow_once() { "var(--bg-btn-emphasis)" } else { "var(--bg-btn)" },
{if follow_once() { rsx!{ "Stop following" } } else { rsx!{ "Follow" } }}
}
div {
height: "100%",
overflow_y: "scroll",
onmounted: move |ev| {
let el = ev.as_web_event();
*scroll_area.write() = Some(el.dyn_into::<web_sys::HtmlElement>().unwrap());
},
div {
onvisible: move |_| {
writeln!(Console, "Top overscroll visible right now");
if top_oscr_visible {
new_top_idx.set(Some(next_top_idx));
follow_once.set(false);
}
},
{top_oscr.iter().map(render_line)}
}
},
{rows.iter().map(|log_line| match &*log_line {
Err(msg) => rsx!{ TextLine { msg: msg } },
Ok(event) => rsx!{ EventLine { event: event.clone() }}
})}
{mid_section.iter().map(render_line)}
div {
onvisible: move |_| if bot_oscr_visible {
new_bot_idx.set(Some(next_bot_idx))
},
{bot_oscr.iter().map(render_line)}
}
div {
height: "5px",
onvisible: move |_| if !follow_once() { follow_once.set(true) },
}
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
use std::range::Range;
use dioxus::prelude::*;
use futures::Stream;
/// The virtual scroller makes the assumption that rendering overhead scales
/// with
#[component]
pub fn virtual_scroll(
wants_prev: Option<Callback<i64, ()>>,
wants_next: Option<Callback<i64, ()>>,
children: Element,
) -> Element {
rsx! {
div {
div {
{children}
}
}
}
}
+10 -14
View File
@@ -1,24 +1,20 @@
use std::cell::RefCell;
use std::time::Duration;
use js_sys::wasm_bindgen::JsCast as _;
use js_sys::wasm_bindgen::prelude::{ScopedClosure, Upcast};
pub fn window() -> web_sys::Window { web_sys::window().unwrap() }
pub fn local_storage() -> web_sys::Storage { window().local_storage().unwrap().unwrap() }
pub fn get_local_storage(key: &str) -> Option<String> { local_storage().get_item(key).unwrap() }
pub fn set_local_storage(key: &str, value: &str) { local_storage().set_item(key, value).unwrap() }
pub fn document() -> web_sys::Document { window().document().unwrap() }
pub fn document_element() -> web_sys::HtmlElement {
document().document_element().unwrap().dyn_into::<web_sys::HtmlElement>().unwrap()
}
fn max_scroll(el: &web_sys::HtmlElement) -> i32 { el.scroll_height() + el.offset_height() }
fn max_scroll(el: &web_sys::HtmlElement) -> i32 { el.scroll_height() - el.offset_height() }
pub fn is_at_bottom(el: &web_sys::HtmlElement, px_tolerance: u32) -> bool {
el.scroll_top().abs_diff(max_scroll(el)) < px_tolerance
}
pub fn scroll_to_bottom(el: &web_sys::HtmlElement) {
pub fn scroll_to_bottom(el: &web_sys::HtmlElement, behavior: web_sys::ScrollBehavior) {
let opt = web_sys::ScrollToOptions::default();
opt.set_behavior(web_sys::ScrollBehavior::Smooth);
opt.set_top(max_scroll(&el) as f64);
opt.set_behavior(behavior);
opt.set_top(max_scroll(el) as f64);
el.scroll_to_with_scroll_to_options(&opt);
}
pub struct Console;
impl Console {
pub fn write_fmt(&self, fmt: std::fmt::Arguments) {
web_sys::console::log_1(&fmt.to_string().into());
}
}
+3 -1
View File
@@ -141,7 +141,9 @@ pub fn init_telemetry(url: impl AsRef<str>, topic: impl AsRef<str>, role: Role)
let url = url.strip_suffix('/').unwrap_or(url);
if role == Role::Leader {
let url = url.strip_prefix("ws").map_or(Cow::Borrowed(url), |s| Cow::Owned(format!("http{s}")));
get_ureq().post(format!("{url}/topic/{topic}/clear")).send_empty().unwrap();
if let Err(e) = get_ureq().post(format!("{url}/topic/{topic}/clear")).send_empty() {
panic!("Could not clear {topic} on {url}; {e}");
}
}
fn encode_event(ev: &TracedEvent) -> Vec<u8> {
let mut vec = Vec::new();
+2 -1
View File
@@ -1,7 +1,8 @@
[package]
name = "xtask"
name = "dev"
version = "0.1.0"
edition = "2024"
[dependencies]
clap = { version = "4.6.1", features = ["derive"] }
itertools = "0.15.0"
File renamed without changes.
+10 -1
View File
@@ -14,6 +14,8 @@ use crate::orcx::{build_orcx_and_std, orcxdb};
pub struct Args {
#[arg(short, long)]
verbose: bool,
#[arg(long)]
dry: bool,
#[command(subcommand)]
command: Commands,
}
@@ -41,7 +43,7 @@ fn main() -> ExitCode {
match run() {
Ok(()) => ExitCode::SUCCESS,
Err(e) => {
eprintln!("xtask error: {e}");
eprintln!("error in cargo-dev: {e}");
ExitCode::FAILURE
},
}
@@ -51,7 +53,14 @@ fn run() -> io::Result<()> {
if let Some(root) = env::var_os("CARGO_WORKSPACE_DIR") {
env::set_current_dir(root)?;
}
eprintln!("Dev toolchain for Orchid");
let args = Args::parse();
if args.dry {
eprintln!(
"All commands are applicable from workspace root ({})",
std::env::current_dir().unwrap().to_string_lossy()
);
}
match &args.command {
Commands::CheckApiRefs => check_api_refs(&args)?,
Commands::BuildOrcx { argv } => build_orcx_and_std(argv, &args)?,
+67
View File
@@ -0,0 +1,67 @@
use std::io::{self, Write, stderr, stdout};
use std::process::Command;
use itertools::Itertools;
use crate::Args;
pub fn print_command(cmd: &Command) -> String {
let args = cmd.get_args().map(|arg| format!(" {}", arg.to_string_lossy())).join("");
let cmd_str = cmd.get_program().to_string_lossy();
let env = cmd
.get_envs()
.map(|(var, val)| {
format!("{}={} ", var.to_string_lossy(), val.map(|v| v.to_string_lossy()).unwrap_or_default())
})
.join("");
let command = format!("{}{}{}", env, cmd_str, args);
match cmd.get_current_dir() {
Some(dir) => format!("(cd {} && {})", dir.to_string_lossy(), command),
None => command,
}
}
pub fn subcommand(cmd: &mut Command, args: &Args) -> io::Result<()> {
if args.dry {
eprintln!("would execute {}", print_command(cmd));
return Ok(());
}
if cmd.status()?.success() {
Ok(())
} else {
stderr().flush()?;
stdout().flush()?;
Err(io::Error::other("Child process failed"))
}
}
pub fn build_orcx_and_std(argv: &[String], args: &Args) -> io::Result<()> {
let mut std_build_cmd = Command::new("cargo");
std_build_cmd.args(["build", "-p", "orchid-std"]);
std_build_cmd.args(argv);
subcommand(&mut std_build_cmd, args)?;
let mut orcx_build_cmd = Command::new("cargo");
orcx_build_cmd.args(["build", "-p", "orcx"]);
orcx_build_cmd.args(argv);
subcommand(&mut orcx_build_cmd, args)
}
pub fn find_orcx_path() -> String {
let exe_name = format!("orcx{}", std::env::consts::EXE_SUFFIX);
let exe_dir = "./target/debug";
format!("{exe_dir}/{exe_name}")
}
pub fn orcx(release: bool, argv: &[String], args: &Args) -> io::Result<()> {
let build_args = if release { &["--release".to_string()][..] } else { &[] };
build_orcx_and_std(build_args, args)?;
let mut orcx_run_cmd = Command::new(find_orcx_path());
orcx_run_cmd.args(argv);
subcommand(&mut orcx_run_cmd, args)
}
pub fn orcxdb(argv: &[String], args: &Args) -> io::Result<()> {
build_orcx_and_std(&[], args)?;
subcommand(Command::new("lldb").args([&find_orcx_path()]).args(argv), args)
}
-39
View File
@@ -1,39 +0,0 @@
use std::io;
use std::process::Command;
use crate::Args;
pub fn subcommand(cmd: &mut Command) -> io::Result<()> {
if cmd.status()?.success() { Ok(()) } else { Err(io::Error::other("Child process failed")) }
}
pub fn build_orcx_and_std(argv: &[String], _args: &Args) -> io::Result<()> {
let mut std_build_cmd = Command::new("cargo");
std_build_cmd.args(["build", "-p", "orchid-std"]);
std_build_cmd.args(argv);
subcommand(&mut std_build_cmd)?;
let mut orcx_build_cmd = Command::new("cargo");
orcx_build_cmd.args(["build", "-p", "orcx"]);
orcx_build_cmd.args(argv);
subcommand(&mut orcx_build_cmd)
}
pub fn find_orcx_path() -> String {
let exe_name = format!("orcx{}", std::env::consts::EXE_SUFFIX);
let exe_dir = "./target/debug";
format!("{exe_dir}/{exe_name}")
}
pub fn orcx(release: bool, argv: &[String], args: &Args) -> io::Result<()> {
let build_args = if release { &["--release".to_string()][..] } else { &[] };
build_orcx_and_std(build_args, args)?;
let mut orcx_run_cmd = Command::new(find_orcx_path());
orcx_run_cmd.args(argv);
subcommand(&mut orcx_run_cmd)
}
pub fn orcxdb(argv: &[String], args: &Args) -> io::Result<()> {
build_orcx_and_std(&[], args)?;
subcommand(Command::new("lldb").args([&find_orcx_path()]).args(argv))
}