Skip to main content

max / audiofiles

Declare the help overlay Three shapes: the overlay, its shortcuts tab and its features tab. The parked `toggling -> Action` stays where it is. The one reshaping is the read. `Chrome::grouped` answers with pairs and the form has no tuple binder, which is the same wall the two MNW consts hit and it has the same answer: `Bound` names the heading and the keys, because a description names what it draws and `.0` is not a name. Everything else was already spellable. The tab strip is a region showing one child, the ungrouped run is a loop over an Option so a missing heading is drawn as no heading rather than as an invented one, and the key tables are two positional columns with an activation per row.
Author: Max Johnson <me@maxj.phd> · 2026-09-04 20:42 UTC
Signed with PGP, not checked
Commit: 9cafb5bd89c316126780a2c98706567c5d651955
Parent: 4a86101
1 file changed, +115 insertions, -84 deletions
@@ -76,10 +76,8 @@
76 76 //! take. Untidy rather than broken, and it is what shipped: the binding
77 77 //! predates this flip, which only made it the sole path to the overlay.
78 78
79 - use quasi_router::{
80 - Action, Cell, Cells, Chrome, Column, Node, RegionKind, Request, Response, RouteError, Router,
81 - Screen, Slot, Table,
82 - };
79 + use quasi_declare::declare;
80 + use quasi_router::{Action, Chrome, Request, Response, RouteError, Router};
83 81
84 82 use super::{Panel, Panels};
85 83
@@ -220,95 +218,128 @@
220 218
221 219 /// `GET /help`
222 220 fn index(_state: &Panels<'_>, _request: Request) -> Result<Response, RouteError> {
223 - Ok(Response::over(screen()))
221 + let chrome = chrome();
222 + Ok(Response::over(screen(&bound(&chrome))))
224 223 }
225 224
226 - /// The overlay.
225 + /// One heading's worth of the shortcuts table.
227 226 ///
228 - /// # Two tabs as one region, not as a control beside one
229 - ///
230 - /// This was a `Node::Select` over a `Slot` the chosen tab's contents were
231 - /// posted into, and it was the last tenant of that member anywhere in the tree
232 - /// (`e9f61b46`, Max: one way to describe a tab strip). A region showing one
233 - /// child at a time says the same thing with nothing beside it: the strip is
234 - /// derived from the children's labels, and which one is up is
235 - /// [`Slot::showing_one`].
236 - ///
237 - /// Both panels are built here rather than fetched, so no child carries
238 - /// [`Slot::fed_by`] and pressing a tab calls no route. They are two static
239 - /// documents a few kilobytes long; a round trip to reveal bytes already in hand
240 - /// is what every renderer's derivation refuses. That is what retired
241 - /// `POST /help/tab` along with the member.
242 - fn screen() -> Screen {
243 - let body = Slot::new(BODY, RegionKind::Pane)
244 - .with(Node::page("audiofiles"))
245 - .with(Node::Region(
246 - Slot::new(TAB, RegionKind::TabGroup)
247 - .showing_one(0)
248 - .with(Node::Region(shortcuts().into_iter().fold(
249 - Slot::new(SHORTCUTS, RegionKind::Group).label("Shortcuts"),
250 - Slot::with,
251 - )))
252 - .with(Node::Region(
253 - Slot::new(FEATURES, RegionKind::Group)
254 - .label("Features")
255 - .with(features()),
256 - )),
257 - ));
258 -
259 - Screen::sidebar_content("Help").with(body)
227 + /// [`Chrome::grouped`] answers with pairs, and a description names what it
228 + /// draws: `.0` is not a name. Same reshaping the two MNW consts took when the
229 + /// form met them.
230 + struct Bound<'a> {
231 + /// The heading, absent for the ungrouped run.
232 + ///
233 + /// This table has none today and would have one again the moment somebody
234 + /// added a `bind`. Drawn without a heading rather than under an invented
235 + /// one, which is the loop over the `Option` in [`shortcuts`].
236 + name: Option<&'a str>,
237 + /// The keys under it, in the order the app bound them.
238 + keys: Vec<&'a quasi_router::chrome::Binding>,
260 239 }
261 240
262 - /// Every key that works, read off the one table.
263 - ///
264 - /// No filter box. The shipped tab has one because twenty-six rows in a
265 - /// fixed-height scroll area need it; narrowing a list a screen was handed is
266 - /// what a host does, which is the rule the bulk port's tag completions and
267 - /// folder filter both follow.
268 - fn shortcuts() -> Vec<Node> {
269 - // A heading and a table per group, in the order the app bound them, which
270 - // is what `Chrome::grouped` answers. Not sorted here: the reading order is
271 - // the shipped tab's and belongs to whoever wrote the table.
272 - //
273 - // Several tables rather than one with a group column, because a group is a
274 - // heading and not a value: a column repeating "Toggles" six times says the
275 - // same thing six times and is still one wall.
276 - let mut sections = Vec::new();
277 - for (group, bindings) in chrome().grouped() {
278 - // `None` is the ungrouped run, which this table has none of today and
279 - // would have again the moment somebody added a `bind`. Drawn without a
280 - // heading rather than under an invented one.
281 - if let Some(name) = group {
282 - sections.push(Node::section(name));
241 + /// The one table, grouped and named.
242 + fn bound(chrome: &Chrome) -> Vec<Bound<'_>> {
243 + chrome
244 + .grouped()
245 + .into_iter()
246 + .map(|(name, keys)| Bound { name, keys })
247 + .collect()
248 + }
249 +
250 + declare! {
251 + /// The overlay.
252 + ///
253 + /// # Two tabs as one region, not as a control beside one
254 + ///
255 + /// This was a `Node::Select` over a `Slot` the chosen tab's contents were
256 + /// posted into, and it was the last tenant of that member anywhere in the
257 + /// tree (`e9f61b46`, Max: one way to describe a tab strip). A region
258 + /// showing one child at a time says the same thing with nothing beside it:
259 + /// the strip is derived from the children's labels, and which one is up is
260 + /// [`Slot::showing_one`].
261 + ///
262 + /// Both panels are built here rather than fetched, so no child carries
263 + /// [`Slot::fed_by`] and pressing a tab calls no route. They are two static
264 + /// documents a few kilobytes long; a round trip to reveal bytes already in
265 + /// hand is what every renderer's derivation refuses. That is what retired
266 + /// `POST /help/tab` along with the member.
267 + shape screen(bound: &[Bound<'_>]) -> Screen;
268 +
269 + screen sidebar_content "Help" {
270 + region BODY as Pane {
271 + page "audiofiles";
272 +
273 + region TAB as TabGroup {
274 + showing_one 0;
275 +
276 + region SHORTCUTS as Group {
277 + label "Shortcuts";
278 + extend shortcuts(bound);
279 + }
280 +
281 + region FEATURES as Group {
282 + label "Features";
283 + include features();
284 + }
285 + }
283 286 }
284 - // Positional cells: the two columns and the two cells are written a
285 - // line apart in the one expression, and a binding is always a key and
286 - // a label, so there is no cell here that appears only sometimes.
287 - //
288 - // No `more`: every key that is bound is listed, which is the whole
289 - // claim of this screen. A shortcuts table with something withheld
290 - // would be the drift it exists to end.
291 - sections.push(Node::from(
292 - Table::new(vec![Column::new("Key"), Column::new("Does")]).rows(bindings.iter().map(
293 - |binding| {
294 - Cells::new(vec![Cell::new(&binding.key), Cell::new(&binding.label)])
295 - .activate(binding.action.clone())
296 - },
297 - )),
298 - ));
299 287 }
300 - sections
301 288 }
302 289
303 - /// What the app does, in prose.
304 - ///
305 - /// One `Node::Rich` rather than nine headings and nine paragraphs, because it is
306 - /// a document: markdown source is what `Rich` carries and every renderer turns
307 - /// it into its own markup, which is the member's whole argument. The shipped tab
308 - /// builds the same thing out of `ui.heading` and `ui.label` calls, so the
309 - /// structure is there and is not written down anywhere a renderer can read.
310 - fn features() -> Node {
311 - Node::rich(FEATURES_MD)
290 + declare! {
291 + /// Every key that works, read off the one table.
292 + ///
293 + /// No filter box. The shipped tab has one because twenty-six rows in a
294 + /// fixed-height scroll area need it; narrowing a list a screen was handed
295 + /// is what a host does, which is the rule the bulk port's tag completions
296 + /// and folder filter both follow.
297 + ///
298 + /// A heading and a table per group, in the order the app bound them. Not
299 + /// sorted here: the reading order is the shipped tab's and belongs to
300 + /// whoever wrote the table. Several tables rather than one with a group
301 + /// column, because a group is a heading and not a value: a column repeating
302 + /// "Toggles" six times says the same thing six times and is still one wall.
303 + ///
304 + /// Positional cells, because a binding is always a key and a label and
305 + /// there is no cell here that appears only sometimes. No `more`: every key
306 + /// that is bound is listed, which is the whole claim of this screen. A
307 + /// shortcuts table with something withheld would be the drift it exists to
308 + /// end.
309 + shape shortcuts(bound: &[Bound<'_>]) -> Vec<Node>;
310 +
311 + for group in bound {
312 + for &name in group.name.iter() {
313 + section name;
314 + }
315 +
316 + table {
317 + column "Key";
318 + column "Does";
319 +
320 + for binding in group.keys.iter() {
321 + cells {
322 + cell &binding.key;
323 + cell &binding.label;
324 + activate to doing binding.action.clone();
325 + }
326 + }
327 + }
328 + }
329 + }
330 +
331 + declare! {
332 + /// What the app does, in prose.
333 + ///
334 + /// One `Node::Rich` rather than nine headings and nine paragraphs, because
335 + /// it is a document: markdown source is what `Rich` carries and every
336 + /// renderer turns it into its own markup, which is the member's whole
337 + /// argument. The shipped tab builds the same thing out of `ui.heading` and
338 + /// `ui.label` calls, so the structure is there and is not written down
339 + /// anywhere a renderer can read.
340 + shape features() -> Node;
341 +
342 + rich FEATURES_MD;
312 343 }
313 344
314 345 /// The features tab, as the document it is.