Skip to main content

max / quasi

0.31.0: a tab strip fetches its panel, it does not reveal it `dfbc88ce`, ruled by Max 2026-08-18 as option (a). Found writing mnw-server's library strip, which is the first description anyone has written with a labelled `Showing::One` region whose children carry addresses. What it did before: the strip was a client-side reveal, buttons carrying `data-shows`, and every child with a `fed_by` emitted `hx-trigger="load"` regardless of its frame being hidden. So a five-tab strip fetched five panels on page load and pressing a tab issued nothing. MNW's library page does one read and four more on demand, so the description as ruled would have cost it four database reads per view. node.rs said so itself -- "route-bound movement available and unbuilt. Nothing asks for it today" -- and this is what asks. The strip carries the address now. Where a child of a labelled selective region carries `fed_by`, that action goes on the tab button and the child emits no load trigger of its own; both marks sit on the button, so htmx makes the request and the binder moves the frame. Presence picks between the two idioms and nothing reads the region's kind, so a carousel of frames already downloaded is untouched. Uniform across every child, the shown one included. `9b958e7b` is why: a screen renders once at final geometry with no placeholder before first content, and a shown panel arriving on `load` is a placeholder on the one panel being looked at. So the shown panel comes with the screen and carries no address, which is what the Askama page it replaces already does with its include. A tab is a `button` even though its address is a read, which inverts `control_tag`'s rule in the one place it should be inverted: a panel route answers with a fragment, so an `href` to one is a link that middle-click and copy-link would follow to a shell-less scrap. `Fires::ClickInStrip` is that, and it is the second member to suppress the href after `Fires::Load`. The retained-screen hosts get it through the router rather than through their own walk. `Screen::feeds` no longer returns a tab's panels, so a terminal or an egui host stops asking for all five on the way up, and `Slot::asked_for` is the address a strip presses. quasi-tui presses it: Left/Right on a strip answers `Step::Call` for a panel that has not arrived, and `Step::Idle` for one that has. quasi-immediate draws no strip yet, so it takes the `feeds` half and nothing else. The walk still descends past a panel, because a panel that arrived can hold a region that genuinely is waiting, and that one is nobody's tab. Not done, and it is written down on `asked_for` rather than smoothed over: a webview re-reads a panel on every press because the markup keeps the address after `Screen::replace` has dropped it from the tree, while a retained host reads it once. Both agree on the thing that mattered.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-19 00:22 UTC
Signed with PGP, not checked
Commit: a2a5aa7680461ca94136dd08f23fc74fadc49b7e
Parent: ada9ad8
18 files changed, +506 insertions, -70 deletions
M Cargo.lock +19 -19
@@ -3404,11 +3404,11 @@
3404 3404
3405 3405 [[package]]
3406 3406 name = "quasi"
3407 - version = "0.30.0"
3407 + version = "0.31.0"
3408 3408
3409 3409 [[package]]
3410 3410 name = "quasi-axum"
3411 - version = "0.30.0"
3411 + version = "0.31.0"
3412 3412 dependencies = [
3413 3413 "axum",
3414 3414 "http",
@@ -3422,7 +3422,7 @@
3422 3422
3423 3423 [[package]]
3424 3424 name = "quasi-basics"
3425 - version = "0.30.0"
3425 + version = "0.31.0"
3426 3426 dependencies = [
3427 3427 "makeover-layout",
3428 3428 "quasi-http",
@@ -3432,7 +3432,7 @@
3432 3432
3433 3433 [[package]]
3434 3434 name = "quasi-bench"
3435 - version = "0.30.0"
3435 + version = "0.31.0"
3436 3436 dependencies = [
3437 3437 "dhat",
3438 3438 "makeover",
@@ -3447,7 +3447,7 @@
3447 3447
3448 3448 [[package]]
3449 3449 name = "quasi-http"
3450 - version = "0.30.0"
3450 + version = "0.31.0"
3451 3451 dependencies = [
3452 3452 "form_urlencoded",
3453 3453 "http",
@@ -3456,7 +3456,7 @@
3456 3456
3457 3457 [[package]]
3458 3458 name = "quasi-immediate"
3459 - version = "0.30.0"
3459 + version = "0.31.0"
3460 3460 dependencies = [
3461 3461 "docengine",
3462 3462 "egui",
@@ -3466,7 +3466,7 @@
3466 3466
3467 3467 [[package]]
3468 3468 name = "quasi-notifs"
3469 - version = "0.30.0"
3469 + version = "0.31.0"
3470 3470 dependencies = [
3471 3471 "quasi-router",
3472 3472 "synckit-config",
@@ -3474,7 +3474,7 @@
3474 3474
3475 3475 [[package]]
3476 3476 name = "quasi-router"
3477 - version = "0.30.0"
3477 + version = "0.31.0"
3478 3478 dependencies = [
3479 3479 "makeover-layout",
3480 3480 ]
@@ -3491,7 +3491,7 @@
3491 3491
3492 3492 [[package]]
3493 3493 name = "quasi-tauri"
3494 - version = "0.30.0"
3494 + version = "0.31.0"
3495 3495 dependencies = [
3496 3496 "http",
3497 3497 "quasi-http",
@@ -3504,7 +3504,7 @@
3504 3504
3505 3505 [[package]]
3506 3506 name = "quasi-tui"
3507 - version = "0.30.0"
3507 + version = "0.31.0"
3508 3508 dependencies = [
3509 3509 "docengine",
3510 3510 "makeover",
@@ -3516,7 +3516,7 @@
3516 3516
3517 3517 [[package]]
3518 3518 name = "quasi-webview"
3519 - version = "0.30.0"
3519 + version = "0.31.0"
3520 3520 dependencies = [
3521 3521 "docengine",
3522 3522 "makeover-layout",
@@ -6187,6 +6187,14 @@
6187 6187 "winnow 1.0.4",
6188 6188 ]
6189 6189
6190 + [[patch.unused]]
6191 + name = "makeover-build"
6192 + version = "0.46.0"
6193 +
6194 + [[patch.unused]]
6195 + name = "quasi-type"
6196 + version = "0.1.0"
6197 +
6190 6198 [[patch.unused]]
6191 6199 name = "synckit-client"
6192 6200 version = "0.8.0"
@@ -6206,11 +6214,3 @@
6206 6214 [[patch.unused]]
6207 6215 name = "tagtree"
6208 6216 version = "0.4.0"
6209 -
6210 - [[patch.unused]]
6211 - name = "quasi-type"
6212 - version = "0.1.0"
6213 -
6214 - [[patch.unused]]
6215 - name = "makeover-build"
6216 - version = "0.46.0"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-axum"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 description = "The axum host adapter for quasi-router: an HTTP request in, a rendered description out"
5 5 edition.workspace = true
6 6 rust-version.workspace = true
@@ -13,14 +13,14 @@
13 13 workspace = true
14 14
15 15 [dependencies]
16 - quasi-router = { path = "../quasi-router", version = "0.30.0" }
17 - quasi-http = { path = "../quasi-http", version = "0.30.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.31.0" }
17 + quasi-http = { path = "../quasi-http", version = "0.31.0" }
18 18 axum = "0.8.8"
19 19 http = "1.3.1"
20 20 tokio = { version = "1.50.0", features = ["rt"] }
21 21
22 22 [dev-dependencies]
23 - quasi-webview = { path = "../quasi-webview", version = "0.30.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.31.0" }
24 24 tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread"] }
25 25 tower = { version = "0.5.3", features = ["util"] }
26 26 http-body-util = "0.1.3"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-basics"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 description = "The first-party widget set: named assemblies of primitives, shared across our apps"
5 5 edition.workspace = true
6 6 rust-version.workspace = true
@@ -13,12 +13,12 @@
13 13 workspace = true
14 14
15 15 [dependencies]
16 - quasi-router = { path = "../quasi-router", version = "0.30.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.31.0" }
17 17 makeover-layout = "0.31.0"
18 18
19 19 [dev-dependencies]
20 20 # A widget's guarantees are claims about what a renderer draws, so they are
21 21 # tested against a real one rather than by walking the tree the assembly just
22 22 # built. The webview is the renderer that recognises names today.
23 - quasi-webview = { path = "../quasi-webview", version = "0.30.0" }
24 - quasi-http = { path = "../quasi-http", version = "0.30.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.31.0" }
24 + quasi-http = { path = "../quasi-http", version = "0.31.0" }
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-bench"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 description = "What a described screen costs to render, in time and in allocations"
5 5 edition.workspace = true
6 6 rust-version.workspace = true
@@ -22,13 +22,13 @@
22 22 count = ["dep:dhat"]
23 23
24 24 [dependencies]
25 - quasi-router = { path = "../quasi-router", version = "0.30.0" }
26 - quasi-webview = { path = "../quasi-webview", version = "0.30.0" }
27 - quasi-tui = { path = "../quasi-tui", version = "0.30.0" }
25 + quasi-router = { path = "../quasi-router", version = "0.31.0" }
26 + quasi-webview = { path = "../quasi-webview", version = "0.31.0" }
27 + quasi-tui = { path = "../quasi-tui", version = "0.31.0" }
28 28 # `Serves` is the trait carrying `screen` and `fragment`, which is what the
29 29 # webview is measured through. Taken directly rather than through quasi-webview
30 30 # because a bench calling a trait method should name the trait it calls.
31 - quasi-http = { path = "../quasi-http", version = "0.30.0" }
31 + quasi-http = { path = "../quasi-http", version = "0.31.0" }
32 32 makeover-layout = "0.31.0"
33 33 makeover-tui = { version = "0.30.0", features = ["theme"] }
34 34 # Only to load a bundled theme file. `makeover_tui::Theme` is `#[non_exhaustive]`,
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-http"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 description = "The http-shaped seam quasi's webview host adapters share: decoding in, a rendered description out"
5 5 edition.workspace = true
6 6 rust-version.workspace = true
@@ -13,6 +13,6 @@
13 13 workspace = true
14 14
15 15 [dependencies]
16 - quasi-router = { path = "../quasi-router", version = "0.30.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.31.0" }
17 17 http = "1.3.1"
18 18 form_urlencoded = "1.2.2"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-immediate"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 edition = "2024"
5 5 description = "The immediate-mode renderer for quasi-router: a described screen in, an egui frame out. Immediate mode is the constraint that matters, not the library."
6 6 license = "MIT"
@@ -12,7 +12,7 @@
12 12 categories = ["gui"]
13 13
14 14 [dependencies]
15 - quasi-router = { path = "../quasi-router", version = "0.30.0" }
15 + quasi-router = { path = "../quasi-router", version = "0.31.0" }
16 16 # The node drawing, which is the makeover layer's and not this crate's. Every
17 17 # widget here that is not a container comes from it: a screen walk that painted
18 18 # its own meter would be the divergence the suite exists to end, one copy per
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-notifs"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 description = "Declared notification kinds and the registry that holds them: what a notification says and what it is for, apart from how it reaches a person"
5 5 edition.workspace = true
6 6 rust-version.workspace = true
@@ -24,5 +24,5 @@
24 24 describe = ["dep:quasi-router"]
25 25
26 26 [dependencies]
27 - quasi-router = { path = "../quasi-router", version = "0.30.0", optional = true }
27 + quasi-router = { path = "../quasi-router", version = "0.31.0", optional = true }
28 28 synckit-config = { git = "https://makenot.work/git/max/synckit.git", version = "0.2", optional = true }
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-router"
3 - version = "0.30.0"
3 + version = "0.31.0"
4 4 description = "Host-agnostic router: a request in, a renderer-agnostic description out"
5 5 edition.workspace = true
6 6 rust-version.workspace = true