max / quasi
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
9 files changed,
+53 insertions,
-71 deletions
| @@ -2059,9 +2059,7 @@ | |||
| 2059 | 2059 | ||
| 2060 | 2060 | [[package]] | |
| 2061 | 2061 | name = "makeover" | |
| 2062 | - | version = "2.5.0" | |
| 2063 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2064 | - | checksum = "3ba9cbc9da41bffe0514e6d6744cecf8097eb18676edcaf62a7dd08cea42e4aa" | |
| 2062 | + | version = "2.5.1" | |
| 2065 | 2063 | dependencies = [ | |
| 2066 | 2064 | "include_dir", | |
| 2067 | 2065 | "serde", | |
| @@ -2071,20 +2069,14 @@ | |||
| 2071 | 2069 | [[package]] | |
| 2072 | 2070 | name = "makeover-geometry" | |
| 2073 | 2071 | version = "0.7.0" | |
| 2074 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2075 | - | checksum = "91a82b16c4f5b66dd7e87ed92fff7d22185e4d88e5355de6166dd64450cf6a16" | |
| 2076 | 2072 | ||
| 2077 | 2073 | [[package]] | |
| 2078 | 2074 | name = "makeover-layout" | |
| 2079 | - | version = "0.18.0" | |
| 2080 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2081 | - | checksum = "48e5725b8dc2d30d5e46b324dd97757e14e61345607c9f55534a34768a6b54dd" | |
| 2075 | + | version = "0.19.0" | |
| 2082 | 2076 | ||
| 2083 | 2077 | [[package]] | |
| 2084 | 2078 | name = "makeover-touch" | |
| 2085 | - | version = "0.11.0" | |
| 2086 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2087 | - | checksum = "aa8a698cf7ba31dc0a2256293139774657ae373425ccd58eabb05431dd283876" | |
| 2079 | + | version = "0.12.0" | |
| 2088 | 2080 | dependencies = [ | |
| 2089 | 2081 | "makeover-geometry", | |
| 2090 | 2082 | "makeover-layout", | |
| @@ -2092,9 +2084,7 @@ | |||
| 2092 | 2084 | ||
| 2093 | 2085 | [[package]] | |
| 2094 | 2086 | name = "makeover-tui" | |
| 2095 | - | version = "0.17.0" | |
| 2096 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2097 | - | checksum = "6bec0a014d4da44d95dca4db82b5f9418edb4dd10fa5009e13832489939fcabd" | |
| 2087 | + | version = "0.18.0" | |
| 2098 | 2088 | dependencies = [ | |
| 2099 | 2089 | "makeover", | |
| 2100 | 2090 | "makeover-layout", | |
| @@ -2103,9 +2093,7 @@ | |||
| 2103 | 2093 | ||
| 2104 | 2094 | [[package]] | |
| 2105 | 2095 | name = "makeover-webview" | |
| 2106 | - | version = "0.34.0" | |
| 2107 | - | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 2108 | - | checksum = "55628364f54e172d255701c3044be78f8a8eff3a2362d9b2960ad8cd54ccf9b7" | |
| 2096 | + | version = "0.35.0" | |
| 2109 | 2097 | dependencies = [ | |
| 2110 | 2098 | "makeover-geometry", | |
| 2111 | 2099 | "makeover-layout", | |
| @@ -5175,6 +5163,18 @@ | |||
| 5175 | 5163 | name = "tagtree" | |
| 5176 | 5164 | version = "0.4.0" | |
| 5177 | 5165 | ||
| 5166 | + | [[patch.unused]] | |
| 5167 | + | name = "alloy_tui" | |
| 5168 | + | version = "7.0.0" | |
| 5169 | + | ||
| 5170 | + | [[patch.unused]] | |
| 5171 | + | name = "makeover-build" | |
| 5172 | + | version = "0.27.0" | |
| 5173 | + | ||
| 5174 | + | [[patch.unused]] | |
| 5175 | + | name = "makeover-immediate" | |
| 5176 | + | version = "0.17.0" | |
| 5177 | + | ||
| 5178 | 5178 | [[patch.unused]] | |
| 5179 | 5179 | name = "synckit-client" | |
| 5180 | 5180 | version = "0.8.0" |
| @@ -13,4 +13,4 @@ | |||
| 13 | 13 | workspace = true | |
| 14 | 14 | ||
| 15 | 15 | [dependencies] | |
| 16 | - | makeover-layout = "0.18.0" | |
| 16 | + | makeover-layout = "0.19.0" |
| @@ -14,13 +14,13 @@ | |||
| 14 | 14 | ||
| 15 | 15 | [dependencies] | |
| 16 | 16 | quasi-router = { path = "../quasi-router", version = "0.1.0" } | |
| 17 | - | makeover-layout = "0.18.0" | |
| 17 | + | makeover-layout = "0.19.0" | |
| 18 | 18 | # The depth palette, the theme bridge and the table. Everything else this crate | |
| 19 | 19 | # draws is written here first and lifted upstream once a second consumer wants | |
| 20 | 20 | # it, which is the order the suite has always moved in: the constrained consumer | |
| 21 | 21 | # finds the shape, and the shared crate takes it after it is known rather than | |
| 22 | 22 | # before. | |
| 23 | - | makeover-tui = { version = "0.17.0", features = ["theme"] } | |
| 23 | + | makeover-tui = { version = "0.18.0", features = ["theme"] } | |
| 24 | 24 | ratatui = { version = "0.30", default-features = false } | |
| 25 | 25 | # `Node::Rich` carries markdown source. A terminal has no markup to hand it to, | |
| 26 | 26 | # so it takes the runs: `render_runs` is the words with the marks that were over |
| @@ -15,8 +15,8 @@ | |||
| 15 | 15 | [dependencies] | |
| 16 | 16 | quasi-router = { path = "../quasi-router", version = "0.1.0" } | |
| 17 | 17 | quasi-http = { path = "../quasi-http", version = "0.1.0" } | |
| 18 | - | makeover-layout = "0.18.0" | |
| 19 | - | makeover-webview = "0.34.0" | |
| 18 | + | makeover-layout = "0.19.0" | |
| 19 | + | makeover-webview = "0.35.0" | |
| 20 | 20 | # `Node::Rich` carries markdown source and this is what turns it into markup. | |
| 21 | 21 | # Sanitising comes with it, which is why the node can carry what a user typed. | |
| 22 | 22 | # |
| @@ -26,6 +26,21 @@ | |||
| 26 | 26 | //! that takes typing. A [`Node::Meter`] and a [`Node::Figure`] are readouts and | |
| 27 | 27 | //! are skipped, and a disabled [`Act`] is drawn and passed over, which is what | |
| 28 | 28 | //! `disabled` means on every host. | |
| 29 | + | //! | |
| 30 | + | //! # Reach is this module; focus is the view's | |
| 31 | + | //! | |
| 32 | + | //! Both are this renderer's, and neither is describable. **Reach** is what this | |
| 33 | + | //! module computes: which things can take focus, and in what order. **Focus** is | |
| 34 | + | //! which reached thing holds the keyboard right now, and it lives in | |
| 35 | + | //! [`crate::View`] because it is a fact about where the user has walked rather | |
| 36 | + | //! than about the screen. The **focus ring** is what the drawing paints on it. | |
| 37 | + | //! | |
| 38 | + | //! A description used to be able to claim focus for a control, and | |
| 39 | + | //! `makeover-layout` removed the member in 0.19.0 on the grounds that focus is | |
| 40 | + | //! fundamentally different per host. The runtime now starts on the first reach | |
| 41 | + | //! unconditionally, which is what it did in practice anyway once the user | |
| 42 | + | //! pressed anything. The three terms are defined once in `makeover_layout`'s | |
| 43 | + | //! crate header, "Reach, focus and the focus ring". | |
| 29 | 44 | ||
| 30 | 45 | use makeover_layout as layout; | |
| 31 | 46 | use quasi_router::{Act, Action, Field, Node, Part, Row, Screen, Slot}; | |
| @@ -44,14 +59,6 @@ | |||
| 44 | 59 | /// The one place the description already anticipated a terminal, and | |
| 45 | 60 | /// the runtime is what finally binds it. | |
| 46 | 61 | key: Option<String>, | |
| 47 | - | /// Whether the description says this is the control to start on. | |
| 48 | - | /// | |
| 49 | - | /// [`layout::State::Focused`] is a fact two parties now claim: the | |
| 50 | - | /// screen says which control matters, and the view holds where the user | |
| 51 | - | /// has walked to since. The rule is that the description decides where | |
| 52 | - | /// focus *starts* and never moves it afterwards, because a redraw that | |
| 53 | - | /// pulled the caret back would take the keyboard off the user. | |
| 54 | - | wants_focus: bool, | |
| 55 | 62 | /// The screen's selection this acts on, if it acts on one. | |
| 56 | 63 | /// | |
| 57 | 64 | /// The commit half of a staged tick. The runtime reads the set the view | |
| @@ -237,7 +244,6 @@ | |||
| 237 | 244 | action, | |
| 238 | 245 | confirm: None, | |
| 239 | 246 | key: None, | |
| 240 | - | wants_focus: false, | |
| 241 | 247 | over: None, | |
| 242 | 248 | }); | |
| 243 | 249 | } | |
| @@ -338,7 +344,6 @@ | |||
| 338 | 344 | action: act.action.clone(), | |
| 339 | 345 | confirm: act.confirm.clone(), | |
| 340 | 346 | key: act.key.clone(), | |
| 341 | - | wants_focus: matches!(act.state, Some(layout::State::Focus)), | |
| 342 | 347 | over: act.over.clone(), | |
| 343 | 348 | }, | |
| 344 | 349 | }); |
| @@ -125,7 +125,6 @@ | |||
| 125 | 125 | saying: None, | |
| 126 | 126 | }; | |
| 127 | 127 | runtime.view.seed(&runtime.screen); | |
| 128 | - | runtime.start_focus(); | |
| 129 | 128 | runtime | |
| 130 | 129 | } | |
| 131 | 130 | ||
| @@ -377,7 +376,6 @@ | |||
| 377 | 376 | // user's ticks are the truth, which is why it is applied once | |
| 378 | 377 | // on arrival rather than read on every draw. | |
| 379 | 378 | self.view.seed(&self.screen); | |
| 380 | - | self.start_focus(); | |
| 381 | 379 | self.announce(); | |
| 382 | 380 | None | |
| 383 | 381 | } | |
| @@ -475,23 +473,6 @@ | |||
| 475 | 473 | } | |
| 476 | 474 | } | |
| 477 | 475 | ||
| 478 | - | /// Start on what the description says matters, when it says anything. | |
| 479 | - | fn start_focus(&mut self) { | |
| 480 | - | let reaches = crate::focus::reaches(&self.screen); | |
| 481 | - | let at = reaches.iter().position(|reach| { | |
| 482 | - | matches!( | |
| 483 | - | reach.spot, | |
| 484 | - | Spot::Act { | |
| 485 | - | wants_focus: true, | |
| 486 | - | .. | |
| 487 | - | } | |
| 488 | - | ) | |
| 489 | - | }); | |
| 490 | - | if let Some(at) = at { | |
| 491 | - | self.view.focus_on(at, reaches.len()); | |
| 492 | - | } | |
| 493 | - | } | |
| 494 | - | ||
| 495 | 476 | /// Type into a field, honouring what the description says it will take. | |
| 496 | 477 | fn type_into(&mut self, field: &crate::FieldSpot, ch: char) { | |
| 497 | 478 | if matches!(field.kind, layout::FieldKind::Checkbox) { |
| @@ -530,17 +530,15 @@ | |||
| 530 | 530 | } | |
| 531 | 531 | ||
| 532 | 532 | #[test] | |
| 533 | - | fn the_description_says_which_control_to_start_on() { | |
| 534 | - | // `layout::State::Focus` is a fact the screen can state, and this is the one | |
| 535 | - | // place it is honoured: where focus starts, never where it goes back to. | |
| 533 | + | fn the_runtime_starts_on_the_first_reach_and_the_description_gets_no_say() { | |
| 534 | + | // The guarantee that replaced `layout::State::Focus`. A description used to | |
| 535 | + | // be able to claim the starting control; focus is this renderer's now, and | |
| 536 | + | // the rule is the plain one: first thing you can reach. | |
| 536 | 537 | let mut runtime = Runtime::new(screen_of([ | |
| 537 | 538 | Node::Act(Act::new("First", Action::post("/first"))), | |
| 538 | - | Node::Act(Act { | |
| 539 | - | state: Some(layout::State::Focus), | |
| 540 | - | ..Act::new("Wanted", Action::post("/wanted")) | |
| 541 | - | }), | |
| 539 | + | Node::Act(Act::new("Second", Action::post("/second"))), | |
| 542 | 540 | ])); | |
| 543 | - | assert_eq!(calling(&runtime.key(Key::Enter)), Some("/wanted")); | |
| 541 | + | assert_eq!(calling(&runtime.key(Key::Enter)), Some("/first")); | |
| 544 | 542 | } | |
| 545 | 543 | ||
| 546 | 544 | #[test] |
| @@ -57,7 +57,12 @@ | |||
| 57 | 57 | /// draws the description's value and the other draws a box the user has | |
| 58 | 58 | /// cleared. | |
| 59 | 59 | edits: BTreeMap<String, String>, | |
| 60 | - | /// Which reachable thing has focus, as an index into [`crate::focus::spots`]. | |
| 60 | + | /// Which reached thing has focus, as an index into [`crate::focus::spots`]. | |
| 61 | + | /// | |
| 62 | + | /// Focus is this renderer's and lives here rather than in a description, | |
| 63 | + | /// which is why it survives a redraw: reach is recomputed from the screen, | |
| 64 | + | /// focus is a fact about where the user has walked. See | |
| 65 | + | /// [`crate::focus`]'s header for the three terms. | |
| 61 | 66 | focus: usize, | |
| 62 | 67 | /// How far each region has been scrolled, in rows, by | |
| 63 | 68 | /// [`Slot::id`](quasi_router::Slot::id). |
| @@ -453,17 +453,10 @@ | |||
| 453 | 453 | out.push_str(" disabled"); | |
| 454 | 454 | } | |
| 455 | 455 | } | |
| 456 | - | Some(layout::State::Focus) => { | |
| 457 | - | out.push_str(" autofocus"); | |
| 458 | - | action_attrs( | |
| 459 | - | &act.action, | |
| 460 | - | Fires::Click, | |
| 461 | - | act.confirm.as_deref(), | |
| 462 | - | morphs, | |
| 463 | - | gathers, | |
| 464 | - | out, | |
| 465 | - | ); | |
| 466 | - | } | |
| 456 | + | // No `autofocus` arm. A description does not state focus: the browser | |
| 457 | + | // owns reach and focus here, which is what `makeover-layout` 0.19.0 | |
| 458 | + | // settled by removing the member this used to read. | |
| 459 | + | // | |
| 467 | 460 | // `State` is `#[non_exhaustive]`, so a member added upstream lands | |
| 468 | 461 | // here. Emitting the transport is the right default for anything that | |
| 469 | 462 | // is not a suppression: a state this renderer has not learned yet |