Skip to main content

max / makeover-webview

Take the curve's granularity, and say what this renderer cannot do with a curve makeover-layout 0.32.0. A range reads `curve.step()`, every other kind keeps `Field::step`, and `Curve::Linear` emits exactly what it emitted before. A constant-ratio curve still emits a linear track, and that is a shortfall rather than a decision. HTML has no logarithmic range input, so honouring one means either shipping JS that maps thumb position to value -- renderer-side app code, which is the thing this stack exists to delete -- or changing what the control submits from a value to a fraction, and then something has to map it on the way back in. That something is not this crate: the MNW server reads these forms with its own handlers, so a fraction arriving where a value is expected would be silent. Filed rather than guessed at. No consumer is affected today; every described range on this path is linear. Also fixes a pre-existing clippy warning in a test that built a `Filling` by reassignment rather than by initializer. 170 tests pass, clippy clean, fmt clean.
Author: Max Johnson <me@maxj.phd> · 2026-08-21 17:37 UTC
Signed with PGP, not checked
Commit: 261bc62485b35cbac5c1fbd266dcf810224134a9
Parent: e96ffb8
3 files changed, +72 insertions, -8 deletions
M Cargo.toml +3 -3
@@ -1,6 +1,6 @@
1 1 [package]
2 2 name = "makeover-webview"
3 - version = "0.53.0"
3 + version = "0.54.0"
4 4 edition = "2024"
5 5 # One copy of this renderer per dependency graph, enforced by cargo rather than
6 6 # by remembering. Two versions means the generated stylesheet and the emitted
@@ -17,7 +17,7 @@
17 17 # patch satisfy the requirement and still fail to compile. That happened once
18 18 # with `form::radio_html` calling `FieldKind::Radio`, and makeover-build is where
19 19 # it surfaced, one release later.
20 - makeover-layout = "0.31.0"
20 + makeover-layout = "0.32.0"
21 21 # The capability axis. `makeover-touch` decides whether a hover rule should be
22 22 # gated at all; `makeover-geometry` spells the gate as a media condition. Both
23 23 # answers are owned elsewhere and neither is re-derived here.
@@ -27,7 +27,7 @@
27 27 # satisfies "0.8" and keeps a second makeover-geometry in the graph next to the
28 28 # 0.7 this crate asks for. `Density` is nominally distinct across the two and
29 29 # the build fails on a type that reads as identical.
30 - makeover-touch = "0.22.0"
30 + makeover-touch = "0.22.1"
31 31 makeover-geometry = "0.7"
32 32
33 33 [lints.rust]
M src/form.rs +48 -5
@@ -318,7 +318,16 @@
318 318 // into a two-position control. That is the granularity the description
319 319 // means when it says nothing, so this is emitted only when an app has said
320 320 // otherwise rather than defaulted here.
321 - if let Some(step) = field.step {
321 + //
322 + // A range takes its granularity from its curve as of makeover-layout
323 + // 0.32.0, and every other kind keeps `Field::step`. See the crate header on
324 + // what this renderer can and cannot do with a curve.
325 + let step = if field.kind == FieldKind::Range {
326 + field.curve.step()
327 + } else {
328 + field.step
329 + };
330 + if let Some(step) = step {
322 331 out.push_str(" step=\"");
323 332 escape_into(step, out);
324 333 out.push('"');
@@ -953,7 +962,7 @@
953 962 #[cfg(test)]
954 963 mod tests {
955 964 use super::*;
956 - use makeover_layout::{Accepted, Family};
965 + use makeover_layout::{Accepted, Curve, Family};
957 966
958 967 fn field(kind: FieldKind) -> Field<'static> {
959 968 Field::new(kind, "title", "Title")
@@ -1002,8 +1011,10 @@
1002 1011 let mut f = field(FieldKind::Radio);
1003 1012 let options = [Choice::new("a", "A")];
1004 1013 f.options = &options;
1005 - let mut filling = Filling::default();
1006 - filling.control_attrs = Some(Markup(r#"data-host="1""#));
1014 + let filling = Filling {
1015 + control_attrs: Some(Markup(r#"data-host="1""#)),
1016 + ..Filling::default()
1017 + };
1007 1018 let html = field_html(&f, &filling, &Emit::default());
1008 1019 assert!(!html.contains("data-host"), "{html}");
1009 1020 }
@@ -1258,7 +1269,7 @@
1258 1269 #[test]
1259 1270 fn a_range_is_a_range_input_and_carries_its_extent() {
1260 1271 let f = Field {
1261 - step: Some("0.01"),
1272 + curve: Curve::Linear { step: Some("0.01") },
1262 1273 ..Field::range("review", "Review above", "0", "1")
1263 1274 };
1264 1275 let html = field_html(&f, &Filling::of(Value::Text("0.72")), &Emit::default());
@@ -1270,6 +1281,38 @@
1270 1281 assert!(html.contains("step=\"0.01\""), "{html}");
1271 1282 }
1272 1283
1284 + #[test]
1285 + fn a_range_reads_its_granularity_off_the_curve_and_not_off_field_step() {
1286 + // The 0.32.0 narrowing, at the renderer. `Field::step` on a range is a
1287 + // site that has not been moved over, and emitting it would make the
1288 + // control step by a number the curve never agreed to.
1289 + let f = Field {
1290 + step: Some("99"),
1291 + ..Field::range("review", "Review above", "0", "1")
1292 + };
1293 + let html = field_html(&f, &Filling::of(Value::Text("0.5")), &Emit::default());
1294 + assert!(!html.contains("step="), "{html}");
1295 + }
1296 +
1297 + #[test]
1298 + fn a_constant_ratio_curve_still_emits_a_linear_track() {
1299 + // Honest shortfall rather than a silent one: HTML has no logarithmic
1300 + // range input, so the browser draws the extent linearly. The value it
1301 + // submits is still a value in the field's own units, which is what
1302 + // every handler on this path reads. See the crate header.
1303 + let f = Field {
1304 + curve: Curve::Logarithmic {
1305 + step: Some("0.001"),
1306 + },
1307 + ..Field::range("attack", "Attack", "0.001", "5")
1308 + };
1309 + let html = field_html(&f, &Filling::of(Value::Text("0.005")), &Emit::default());
1310 + assert!(html.contains("type=\"range\""), "{html}");
1311 + assert!(html.contains("min=\"0.001\""), "{html}");
1312 + assert!(html.contains("max=\"5\""), "{html}");
1313 + assert!(html.contains("step=\"0.001\""), "{html}");
1314 + }
1315 +
1273 1316 #[test]
1274 1317 fn a_number_with_bounds_is_still_typed_into() {
1275 1318 // The distinction the kind exists for, at the renderer where getting it
M src/lib.rs +21
@@ -166,6 +166,27 @@
166 166 //! the same magnitude with its sign off the depth. Both values are the measured
167 167 //! consensus rather than a new opinion.
168 168 //!
169 + //! # 0.54.0: a curve this renderer can carry, and one it cannot
170 + //!
171 + //! `makeover-layout` 0.32.0's `Curve`. A range takes its granularity from the
172 + //! curve now (`Field::curve.step()`), every other kind keeps `Field::step`, and
173 + //! `Curve::Linear` emits exactly what it emitted before.
174 + //!
175 + //! **A constant-ratio curve still emits a linear track, and that is a
176 + //! shortfall rather than a decision.** HTML has no logarithmic range input, so
177 + //! honouring one here means either shipping JS that maps the thumb position to
178 + //! a value -- renderer-side app code, which is the thing this stack exists to
179 + //! delete -- or changing what the control submits from a value to a fraction,
180 + //! and then something has to map it on the way back in. That something is not
181 + //! this crate: the MNW server reads these forms with its own handlers, so a
182 + //! fraction arriving where a value is expected would be silent. Filed rather
183 + //! than guessed at.
184 + //!
185 + //! What is not lost: the extent, the granularity, and the value's own units.
186 + //! What is lost is resolution at the small end, which is exactly the reason an
187 + //! app asked for a ratio in the first place. No consumer is affected today --
188 + //! every described range on this path is linear.
189 + //!
169 190 //! # 0.52.0: a markdown field gets the preview it was permitted
170 191 //!
171 192 //! 0.50.0 marked a [`makeover_layout::FieldKind::Rich`] field with