Skip to main content

max / quasi

0.52.0: a box with the focus keeps the letters a binding would have taken bc5528e2, found 2026-08-16 porting audiofiles' help overlay -- the first consumer Chrome ever had. Runtime::pressed_binding read raw input at the top of show, before the screen drew, and matched every binding with no focus guard. Sixteen of audiofiles' twenty-six shortcuts are bare letters, and declaring any of them meant the tag field, the rename pattern and the search box stopped accepting that letter. The port declared four -- f1, f2, ctrl+t, ctrl+shift+m -- and left the rest unsaid. The guard is the renderer's, as the finding argued: which keys a box eats is a fact about this keyboard and this host, so a Binding that answered it would be answering for hosts it has never seen. ctx.text_edit_focused() plus types(), which is narrower than the shipped app's rule in both directions -- that one suppresses everything while anything has focus, so it kills ctrl+t mid-word and fires on a button reached by Tab. Matching goes exact in the same pass, and it is a defect the guard exposed rather than one it caused. matches_logically ignores a shift the pattern never asked for, which is right for one control's Act::key and wrong for a table: an app binding both f and shift+f had the bare one answer both, first match wins. Nobody could see it while bare letters were undeclarable. Three tests, one driving two frames through one context so the focus under test is real rather than stubbed. Consumers forward-fixed in the same pass: audiofiles takes it and its chrome goes from four rows to thirteen; MNW server, goingson and the scaffolder's template move to 0.52.
Author: Max Johnson <me@maxj.phd> · 2026-08-22 03:31 UTC
Signed with PGP, not checked
Commit: 42d92638b2a736ca654746c9e2e0a96d8f266eae
Parent: e01c9ec
17 files changed, +280 insertions, -62 deletions
M Cargo.lock +29 -23
@@ -2589,6 +2589,8 @@
2589 2589 [[package]]
2590 2590 name = "makeover"
2591 2591 version = "3.0.0"
2592 + source = "registry+https://github.com/rust-lang/crates.io-index"
2593 + checksum = "3e6da78c376074e48eb2324394004afc847e674e05ed2ea4bec1331073990b3f"
2592 2594 dependencies = [
2593 2595 "include_dir",
2594 2596 "serde",
@@ -2598,10 +2600,14 @@
2598 2600 [[package]]
2599 2601 name = "makeover-geometry"
2600 2602 version = "0.7.0"
2603 + source = "registry+https://github.com/rust-lang/crates.io-index"
2604 + checksum = "91a82b16c4f5b66dd7e87ed92fff7d22185e4d88e5355de6166dd64450cf6a16"
2601 2605
2602 2606 [[package]]
2603 2607 name = "makeover-immediate"
2604 2608 version = "0.33.0"
2609 + source = "registry+https://github.com/rust-lang/crates.io-index"
2610 + checksum = "43d67f743414e6b1ffcce6da7e9500cd2d4efe5cf42d730ae8f6e6c8d87c3d5a"
2605 2611 dependencies = [
2606 2612 "egui",
2607 2613 "egui_extras",
@@ -2611,10 +2617,14 @@
2611 2617 [[package]]
2612 2618 name = "makeover-layout"
2613 2619 version = "0.35.0"
2620 + source = "registry+https://github.com/rust-lang/crates.io-index"
2621 + checksum = "774bfd81b003f0d0059ff50daa8e625e6969175bc0132ddf7c2108bba40c47d5"
2614 2622
2615 2623 [[package]]
2616 2624 name = "makeover-touch"
2617 2625 version = "0.24.0"
2626 + source = "registry+https://github.com/rust-lang/crates.io-index"
2627 + checksum = "75c49f37baa4360fc06e87834a39978b8adc70c256430864a8b234fcf2abfa45"
2618 2628 dependencies = [
2619 2629 "makeover-geometry",
2620 2630 "makeover-layout",
@@ -2623,6 +2633,8 @@
2623 2633 [[package]]
2624 2634 name = "makeover-tui"
2625 2635 version = "0.33.0"
2636 + source = "registry+https://github.com/rust-lang/crates.io-index"
2637 + checksum = "5196506074bbf9f2b9955ff9e94892e2eca85d9dac5af49ed93da30d50d6c622"
2626 2638 dependencies = [
2627 2639 "makeover",
2628 2640 "makeover-layout",
@@ -2632,6 +2644,8 @@
2632 2644 [[package]]
2633 2645 name = "makeover-webview"
2634 2646 version = "0.57.0"
2647 + source = "registry+https://github.com/rust-lang/crates.io-index"
2648 + checksum = "de975123768cac0c56ae40479cac721fe8498e0fd50e643e6884799d8eb12d75"
2635 2649 dependencies = [
2636 2650 "makeover-geometry",
2637 2651 "makeover-layout",
@@ -3400,11 +3414,11 @@
3400 3414
3401 3415 [[package]]
3402 3416 name = "quasi"
3403 - version = "0.51.0"
3417 + version = "0.52.0"
3404 3418
3405 3419 [[package]]
3406 3420 name = "quasi-axum"
3407 - version = "0.51.0"
3421 + version = "0.52.0"
3408 3422 dependencies = [
3409 3423 "axum",
3410 3424 "http",
@@ -3418,7 +3432,7 @@
3418 3432
3419 3433 [[package]]
3420 3434 name = "quasi-basics"
3421 - version = "0.51.0"
3435 + version = "0.52.0"
3422 3436 dependencies = [
3423 3437 "makeover-layout",
3424 3438 "quasi-http",
@@ -3428,7 +3442,7 @@
3428 3442
3429 3443 [[package]]
3430 3444 name = "quasi-bench"
3431 - version = "0.51.0"
3445 + version = "0.52.0"
3432 3446 dependencies = [
3433 3447 "dhat",
3434 3448 "makeover",
@@ -3443,7 +3457,7 @@
3443 3457
3444 3458 [[package]]
3445 3459 name = "quasi-http"
3446 - version = "0.51.0"
3460 + version = "0.52.0"
3447 3461 dependencies = [
3448 3462 "form_urlencoded",
3449 3463 "http",
@@ -3452,7 +3466,7 @@
3452 3466
3453 3467 [[package]]
3454 3468 name = "quasi-immediate"
3455 - version = "0.51.0"
3469 + version = "0.52.0"
3456 3470 dependencies = [
3457 3471 "docengine",
3458 3472 "egui",
@@ -3462,7 +3476,7 @@
3462 3476
3463 3477 [[package]]
3464 3478 name = "quasi-notifs"
3465 - version = "0.51.0"
3479 + version = "0.52.0"
3466 3480 dependencies = [
3467 3481 "quasi-router",
3468 3482 "synckit-config",
@@ -3470,7 +3484,7 @@
3470 3484
3471 3485 [[package]]
3472 3486 name = "quasi-router"
3473 - version = "0.51.0"
3487 + version = "0.52.0"
3474 3488 dependencies = [
3475 3489 "makeover-layout",
3476 3490 ]
@@ -3487,7 +3501,7 @@
3487 3501
3488 3502 [[package]]
3489 3503 name = "quasi-tauri"
3490 - version = "0.51.0"
3504 + version = "0.52.0"
3491 3505 dependencies = [
3492 3506 "http",
3493 3507 "quasi-http",
@@ -3500,7 +3514,7 @@
3500 3514
3501 3515 [[package]]
3502 3516 name = "quasi-tui"
3503 - version = "0.51.0"
3517 + version = "0.52.0"
3504 3518 dependencies = [
3505 3519 "docengine",
3506 3520 "makeover",
@@ -3512,7 +3526,7 @@
3512 3526
3513 3527 [[package]]
3514 3528 name = "quasi-webview"
3515 - version = "0.51.0"
3529 + version = "0.52.0"
3516 3530 dependencies = [
3517 3531 "docengine",
3518 3532 "makeover-layout",
@@ -6184,12 +6198,12 @@
6184 6198 ]
6185 6199
6186 6200 [[patch.unused]]
6187 - name = "makeover-build"
6188 - version = "0.49.0"
6201 + name = "quasi-type"
6202 + version = "0.1.0"
6189 6203
6190 6204 [[patch.unused]]
6191 - name = "makeover-timing"
6192 - version = "0.1.0"
6205 + name = "synckit-client"
6206 + version = "0.8.1"
6193 6207
6194 6208 [[patch.unused]]
6195 6209 name = "kberg"
@@ -6206,11 +6220,3 @@
6206 6220 [[patch.unused]]
6207 6221 name = "tagtree"
6208 6222 version = "0.4.1"
6209 -
6210 - [[patch.unused]]
6211 - name = "synckit-client"
6212 - version = "0.8.1"
6213 -
6214 - [[patch.unused]]
6215 - name = "quasi-type"
6216 - version = "0.1.0"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-axum"
3 - version = "0.51.0"
3 + version = "0.52.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.51.0" }
17 - quasi-http = { path = "../quasi-http", version = "0.51.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.52.0" }
17 + quasi-http = { path = "../quasi-http", version = "0.52.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.51.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.52.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.51.0"
3 + version = "0.52.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.51.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.52.0" }
17 17 makeover-layout = "0.35.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.51.0" }
24 - quasi-http = { path = "../quasi-http", version = "0.51.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.52.0" }
24 + quasi-http = { path = "../quasi-http", version = "0.52.0" }
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-bench"
3 - version = "0.51.0"
3 + version = "0.52.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.51.0" }
26 - quasi-webview = { path = "../quasi-webview", version = "0.51.0" }
27 - quasi-tui = { path = "../quasi-tui", version = "0.51.0" }
25 + quasi-router = { path = "../quasi-router", version = "0.52.0" }
26 + quasi-webview = { path = "../quasi-webview", version = "0.52.0" }
27 + quasi-tui = { path = "../quasi-tui", version = "0.52.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.51.0" }
31 + quasi-http = { path = "../quasi-http", version = "0.52.0" }
32 32 makeover-layout = "0.35.0"
33 33 makeover-tui = { version = "0.33.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.51.0"
3 + version = "0.52.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.51.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.52.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.51.0"
3 + version = "0.52.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.51.0" }
15 + quasi-router = { path = "../quasi-router", version = "0.52.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.51.0"
3 + version = "0.52.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.51.0", optional = true }
27 + quasi-router = { path = "../quasi-router", version = "0.52.0", optional = true }
28 28 synckit-config = { git = "https://makenot.work/git/max/synckit.git", version = "0.2", optional = true }