Skip to main content

max / quasi

0.14.0: a renderer says how big a selection is, and will not commit an empty one Two things every bulk bar does and no description can say. The ticks on a screen are the host's until something submits them -- which is why `Act::over` gathers them at press time rather than the handler being told up front -- so a route answering from the store cannot know how many are set, and a commit control over an empty selection was offered, pressed, and answered "0 rows touched". A screen letting the reader find out by trying. Each renderer says both with what it already holds. The terminal and egui hold the set in their own `View`, so it is a count on the control's own label and a `State::Disabled` when the count is zero, plus a refused press and a refused key. The webview does not hold it -- the ticks are DOM state -- so this crate now ships `SELECTION_JS`, twenty lines reading two hooks the emitter already writes: `.row-select` on a tick and a new `data-over` on a control that commits one. `Shell::selection_src` says where a host serves it, `without_selection` drops it, and the fallback is safe rather than silent: a control stays enabled and says no count, which is what every screen did yesterday. That is the difference from `morph_src`, whose absence makes htmx swap destructively. The count rides on the control's label rather than in a status line, because a status line needs somewhere to go and nothing in the description says where. On the control it is unambiguous besides: it is the number this press would act on. Only a control that names a verb takes it -- a picker's words are a question, and a question with a count after it reads as one more option. Disabled rather than hidden. `bulk-actions.js` hides its bar and can afford to because its rows keep their checkboxes; a bar that vanishes takes with it the only evidence that bulk actions exist. `Act::over`'s doc gains the other half: a handler still answers for an empty set. A renderer's refusal is an affordance, not a guarantee about what arrives.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-16 15:48 UTC
Signed with PGP, not checked
Commit: 0ccc163ab1686ff785f61b43f79b5594af4fc3d6
Parent: 180cf5b
22 files changed, +511 insertions, -58 deletions
M Cargo.lock +18 -18
@@ -3092,11 +3092,11 @@
3092 3092
3093 3093 [[package]]
3094 3094 name = "quasi"
3095 - version = "0.13.0"
3095 + version = "0.14.0"
3096 3096
3097 3097 [[package]]
3098 3098 name = "quasi-axum"
3099 - version = "0.13.0"
3099 + version = "0.14.0"
3100 3100 dependencies = [
3101 3101 "axum",
3102 3102 "http",
@@ -3110,7 +3110,7 @@
3110 3110
3111 3111 [[package]]
3112 3112 name = "quasi-basics"
3113 - version = "0.13.0"
3113 + version = "0.14.0"
3114 3114 dependencies = [
3115 3115 "makeover-layout",
3116 3116 "quasi-http",
@@ -3120,7 +3120,7 @@
3120 3120
3121 3121 [[package]]
3122 3122 name = "quasi-bench"
3123 - version = "0.13.0"
3123 + version = "0.14.0"
3124 3124 dependencies = [
3125 3125 "dhat",
3126 3126 "makeover",
@@ -3135,7 +3135,7 @@
3135 3135
3136 3136 [[package]]
3137 3137 name = "quasi-http"
3138 - version = "0.13.0"
3138 + version = "0.14.0"
3139 3139 dependencies = [
3140 3140 "form_urlencoded",
3141 3141 "http",
@@ -3144,7 +3144,7 @@
3144 3144
3145 3145 [[package]]
3146 3146 name = "quasi-immediate"
3147 - version = "0.13.0"
3147 + version = "0.14.0"
3148 3148 dependencies = [
3149 3149 "docengine",
3150 3150 "egui",
@@ -3154,7 +3154,7 @@
3154 3154
3155 3155 [[package]]
3156 3156 name = "quasi-router"
3157 - version = "0.13.0"
3157 + version = "0.14.0"
3158 3158 dependencies = [
3159 3159 "makeover-layout",
3160 3160 ]
@@ -3171,7 +3171,7 @@
3171 3171
3172 3172 [[package]]
3173 3173 name = "quasi-tauri"
3174 - version = "0.13.0"
3174 + version = "0.14.0"
3175 3175 dependencies = [
3176 3176 "http",
3177 3177 "quasi-http",
@@ -3182,7 +3182,7 @@
3182 3182
3183 3183 [[package]]
3184 3184 name = "quasi-tui"
3185 - version = "0.13.0"
3185 + version = "0.14.0"
3186 3186 dependencies = [
3187 3187 "docengine",
3188 3188 "makeover",
@@ -3194,7 +3194,7 @@
3194 3194
3195 3195 [[package]]
3196 3196 name = "quasi-webview"
3197 - version = "0.13.0"
3197 + version = "0.14.0"
3198 3198 dependencies = [
3199 3199 "docengine",
3200 3200 "makeover-layout",
@@ -5624,14 +5624,6 @@
5624 5624 source = "registry+https://github.com/rust-lang/crates.io-index"
5625 5625 checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
5626 5626
5627 - [[patch.unused]]
5628 - name = "synckit-client"
5629 - version = "0.8.0"
5630 -
5631 - [[patch.unused]]
5632 - name = "synckit-config"
5633 - version = "0.2.0"
5634 -
5635 5627 [[patch.unused]]
5636 5628 name = "kberg"
5637 5629 version = "0.1.0"
@@ -5647,3 +5639,11 @@
5647 5639 [[patch.unused]]
5648 5640 name = "tagtree"
5649 5641 version = "0.4.0"
5642 +
5643 + [[patch.unused]]
5644 + name = "synckit-client"
5645 + version = "0.8.0"
5646 +
5647 + [[patch.unused]]
5648 + name = "synckit-config"
5649 + version = "0.2.0"
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-axum"
3 - version = "0.13.0"
3 + version = "0.14.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.13.0" }
17 - quasi-http = { path = "../quasi-http", version = "0.13.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.14.0" }
17 + quasi-http = { path = "../quasi-http", version = "0.14.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.13.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.14.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.13.0"
3 + version = "0.14.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.13.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.14.0" }
17 17 makeover-layout = "0.27.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.13.0" }
24 - quasi-http = { path = "../quasi-http", version = "0.13.0" }
23 + quasi-webview = { path = "../quasi-webview", version = "0.14.0" }
24 + quasi-http = { path = "../quasi-http", version = "0.14.0" }
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "quasi-bench"
3 - version = "0.13.0"
3 + version = "0.14.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.13.0" }
26 - quasi-webview = { path = "../quasi-webview", version = "0.13.0" }
27 - quasi-tui = { path = "../quasi-tui", version = "0.13.0" }
25 + quasi-router = { path = "../quasi-router", version = "0.14.0" }
26 + quasi-webview = { path = "../quasi-webview", version = "0.14.0" }
27 + quasi-tui = { path = "../quasi-tui", version = "0.14.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.13.0" }
31 + quasi-http = { path = "../quasi-http", version = "0.14.0" }
32 32 makeover-layout = "0.27.0"
33 33 makeover-tui = { version = "0.25.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.13.0"
3 + version = "0.14.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.13.0" }
16 + quasi-router = { path = "../quasi-router", version = "0.14.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.13.0"
3 + version = "0.14.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.13.0" }
15 + quasi-router = { path = "../quasi-router", version = "0.14.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-router"
3 - version = "0.13.0"
3 + version = "0.14.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