Skip to main content

max / quasi

0.54.0: an act deposits a value into a field, and a binding carries its heading Two vocabulary members, both ruled and both with a measured consumer, plus an accessibility fix a parallel session landed in the same tree (see the last section). f35aafee, ruled (a) by Max 2026-08-21. Nothing could say "take this string and put it into that OTHER field". A chosen thing landed in exactly two places: the server wrote it and answered a screen or a fragment, or a Field carried `changes` and sent its own value. MNW's media-picker.js does the third thing across three templates and 155 lines, and it is the entire point of the button. Act::fills: Option<Fill>, Fill { field, value }, Act::filling(field, value). The ruling suggested fills: Option<String> and that names a destination with nothing to put in it -- an Act carries a label and an address and no value, and the picker's card reads as kick.png while depositing ![](media/kick.png). Both halves, the way Candidate splits value from label. field is addressed by Field::name, so no id relationship is invented. A deposit does not replace the action: the renderer fills and then dispatches Act::action as it always would, and an act that only fills says so with Destination::Local. Fill-after-gather, fixed by the webview rather than chosen -- htmx's listener sits on the control and the fill script on the document, so the element handler runs first, and the two client renderers match it deliberately. d52884b0 stands untouched, which is why (a) rather than (b): nothing here carries a caret in either direction. A webview inserts at the selection, the TUI and egui append, and the vocabulary calls appending correct rather than a fallback. The webview half is FILL_JS and not a hyperscript program. That file's one standing rule is that no program is built out of text a user typed, and the deposited value is exactly that. As two escaped attributes it is data. The script inserts at selectionStart and dispatches `input`, without which the three measured surfaces show the reference and save a document without it: the blog editor autosaves on a 30s debounce, the item text editor has no autosave, and item_details' section bodies are unsaved by construction. Losing the caret is a downgrade; losing the draft is a defect. cf7872dc, split out of a7f0c6c0. Binding::label exists so a help overlay is not a second hand-written copy of the key table, and that argument runs out at length: audiofiles' described table was four rows until 0.52.0's focus guard let the bare letters be declared, and thirteen is the same wall the shipped tab broke into seven arrays. Binding::group, Chrome::bind_in, Chrome::grouped. Said by the app, never derived. Sorting by key puts "Toggle the sidebar" beside "Show this help", and deriving from the address prefix is the same guess wearing a path: /panels/sidebar and /forge are both Toggles to a reader and siblings in nothing. The grouping is Chrome's and not the renderers'. The task asked for "all three renderers group a help listing by it" and no renderer draws one -- a help screen is a described screen like any other, which is what Binding::label bought. Chrome::grouped is the shared half that stops the three renderers and every app that draws its own table each writing the ordering. Consumers forward-fixed in the same pass: audiofiles, goingson and the MNW server all take 0.54, and audiofiles' help.rs binds under Bulk, Discovery, Toggles and System and draws a heading and a table per group. Found beside it: Shell links quasi-download.js by default and neither goingson nor the MNW server was writing the file out, so every described document has been linking a 404 since that script shipped. Both now write the renderer's scripts in a loop rather than one call per name, which is what stops the next one going the same way. 461582b5, carried here rather than split. quasi-immediate's node::row claimed a pressable row's strip with a bare ui.interact and never called Response::widget_info, and egui builds its AccessKit tree from WidgetInfo alone -- so a row that opens worked under a mouse and did not exist for a keyboard or a screen reader. row_name plus one widget_info call, with two tests. Found by the parallel session flipping audiofiles' tag queue, where it left no non-mouse way to open a tag group; it lands in this commit because node.rs also carries the deposit above and splitting the file would have been worse than saying so. The table path has the same bare interact and is deliberately not fixed: it is claimed per cell, so naming each cell a button announces N buttons per row, and a row-wide response needs makeover-immediate's table to hand one back. That half stays on the problem.
Wiki
quasi-vocabulary-findings, two new sections.
Author: Max Johnson <me@maxj.phd> · 2026-08-22 17:43 UTC
Signed with PGP, not checked
Commit: 136b2aaa4f8c17791a146f88ce16cebc28dc7c58
Parent: 219cb84
30 files changed, +937 insertions, -77 deletions
M Cargo.lock +15 -15
@@ -3414,11 +3414,11 @@
3414 3414
3415 3415 [[package]]
3416 3416 name = "quasi"
3417 - version = "0.53.0"
3417 + version = "0.54.0"
3418 3418
3419 3419 [[package]]
3420 3420 name = "quasi-axum"
3421 - version = "0.53.0"
3421 + version = "0.54.0"
3422 3422 dependencies = [
3423 3423 "axum",
3424 3424 "http",
@@ -3432,7 +3432,7 @@
3432 3432
3433 3433 [[package]]
3434 3434 name = "quasi-basics"
3435 - version = "0.53.0"
3435 + version = "0.54.0"
3436 3436 dependencies = [
3437 3437 "makeover-layout",
3438 3438 "quasi-http",
@@ -3442,7 +3442,7 @@
3442 3442
3443 3443 [[package]]
3444 3444 name = "quasi-bench"
3445 - version = "0.53.0"
3445 + version = "0.54.0"
3446 3446 dependencies = [
3447 3447 "dhat",
3448 3448 "makeover",
@@ -3457,7 +3457,7 @@
3457 3457
3458 3458 [[package]]
3459 3459 name = "quasi-http"
3460 - version = "0.53.0"
3460 + version = "0.54.0"
3461 3461 dependencies = [
3462 3462 "form_urlencoded",
3463 3463 "http",
@@ -3466,7 +3466,7 @@
3466 3466
3467 3467 [[package]]
3468 3468 name = "quasi-immediate"
3469 - version = "0.53.0"
3469 + version = "0.54.0"
3470 3470 dependencies = [
3471 3471 "docengine",
3472 3472 "egui",
@@ -3476,7 +3476,7 @@
3476 3476
3477 3477 [[package]]
3478 3478 name = "quasi-notifs"
3479 - version = "0.53.0"
3479 + version = "0.54.0"
3480 3480 dependencies = [
3481 3481 "quasi-router",
3482 3482 "synckit-config",
@@ -3484,7 +3484,7 @@
3484 3484
3485 3485 [[package]]
3486 3486 name = "quasi-router"
3487 - version = "0.53.0"
3487 + version = "0.54.0"
3488 3488 dependencies = [
3489 3489 "makeover-layout",
3490 3490 ]
@@ -3501,7 +3501,7 @@
3501 3501
3502 3502 [[package]]
3503 3503 name = "quasi-tauri"
3504 - version = "0.53.0"
3504 + version = "0.54.0"
3505 3505 dependencies = [
3506 3506 "http",
3507 3507 "quasi-http",
@@ -3514,7 +3514,7 @@
3514 3514
3515 3515 [[package]]
3516 3516 name = "quasi-tui"
3517 - version = "0.53.0"
3517 + version = "0.54.0"
3518 3518 dependencies = [
3519 3519 "docengine",
3520 3520 "makeover",
@@ -3526,7 +3526,7 @@
3526 3526
3527 3527 [[package]]
3528 3528 name = "quasi-webview"
3529 - version = "0.53.0"
3529 + version = "0.54.0"
3530 3530 dependencies = [
3531 3531 "docengine",
3532 3532 "makeover-layout",
@@ -6198,8 +6198,8 @@
6198 6198 ]
6199 6199
6200 6200 [[patch.unused]]
6201 - name = "synckit-client"
6202 - version = "0.8.1"
6201 + name = "quasi-type"
6202 + version = "0.1.0"
6203 6203
6204 6204 [[patch.unused]]
6205 6205 name = "kberg"
@@ -6218,5 +6218,5 @@
6218 6218 version = "0.4.1"
6219 6219
6220 6220 [[patch.unused]]
6221 - name = "quasi-type"
6222 - version = "0.1.0"
6221 + name = "synckit-client"
6222 + version = "0.8.1"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-axum"
3 - version = "0.53.0"
3 + version = "0.54.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.53.0" }
17 - quasi-http = { path = "../quasi-http", version = "0.53.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.54.0" }
17 + quasi-http = { path = "../quasi-http", version = "0.54.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.53.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.54.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.53.0"
3 + version = "0.54.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.53.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.54.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.53.0" }
24 - quasi-http = { path = "../quasi-http", version = "0.53.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.54.0" }
24 + quasi-http = { path = "../quasi-http", version = "0.54.0" }
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-bench"
3 - version = "0.53.0"
3 + version = "0.54.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.53.0" }
26 - quasi-webview = { path = "../quasi-webview", version = "0.53.0" }
27 - quasi-tui = { path = "../quasi-tui", version = "0.53.0" }
25 + quasi-router = { path = "../quasi-router", version = "0.54.0" }
26 + quasi-webview = { path = "../quasi-webview", version = "0.54.0" }
27 + quasi-tui = { path = "../quasi-tui", version = "0.54.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.53.0" }
31 + quasi-http = { path = "../quasi-http", version = "0.54.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.53.0"
3 + version = "0.54.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.53.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.54.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.53.0"
3 + version = "0.54.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.53.0" }
15 + quasi-router = { path = "../quasi-router", version = "0.54.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.53.0"
3 + version = "0.54.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.53.0", optional = true }
27 + quasi-router = { path = "../quasi-router", version = "0.54.0", optional = true }
28 28 synckit-config = { git = "https://makenot.work/git/max/synckit.git", version = "0.2", optional = true }