Skip to main content

max / audiofiles

Take quasi 0.83.0, where a notice can carry an act Twelve Node::Notice struct literals became Node::banner or Node::toast. The constructors were always there; the literals only existed because nothing forced the choice, and a member added upstream is what forces it. None of the twelve has anything to do about it, so none grows an act.
Author: Max Johnson <me@maxj.phd> · 2026-08-30 19:46 UTC
Signed with PGP, not checked
Commit: 868ce855bd570b3bc2f6053f1c53f588f05e9ee5
Parent: 9398809
11 files changed, +69 insertions, -110 deletions
M Cargo.lock +30 -30
@@ -4256,7 +4256,7 @@
4256 4256
4257 4257 [[package]]
4258 4258 name = "quasi-immediate"
4259 - version = "0.82.0"
4259 + version = "0.83.0"
4260 4260 dependencies = [
4261 4261 "docengine",
4262 4262 "egui",
@@ -4267,7 +4267,7 @@
4267 4267
4268 4268 [[package]]
4269 4269 name = "quasi-router"
4270 - version = "0.82.0"
4270 + version = "0.83.0"
4271 4271 dependencies = [
4272 4272 "makeover-layout",
4273 4273 ]
@@ -7556,34 +7556,6 @@
7556 7556 "winnow 1.0.4",
7557 7557 ]
7558 7558
7559 - [[patch.unused]]
7560 - name = "quasi-axum"
7561 - version = "0.82.0"
7562 -
7563 - [[patch.unused]]
7564 - name = "quasi-basics"
7565 - version = "0.82.0"
7566 -
7567 - [[patch.unused]]
7568 - name = "quasi-http"
7569 - version = "0.82.0"
7570 -
7571 - [[patch.unused]]
7572 - name = "quasi-notifs"
7573 - version = "0.82.0"
7574 -
7575 - [[patch.unused]]
7576 - name = "quasi-store"
7577 - version = "0.1.0"
7578 -
7579 - [[patch.unused]]
7580 - name = "quasi-tauri"
7581 - version = "0.82.0"
7582 -
7583 - [[patch.unused]]
7584 - name = "quasi-webview"
7585 - version = "0.82.0"
7586 -
7587 7559 [[patch.unused]]
7588 7560 name = "kberg"
7589 7561 version = "0.1.0"
@@ -7599,3 +7571,31 @@
7599 7571 [[patch.unused]]
7600 7572 name = "quasi-type"
7601 7573 version = "0.1.3"
7574 +
7575 + [[patch.unused]]
7576 + name = "quasi-axum"
7577 + version = "0.83.0"
7578 +
7579 + [[patch.unused]]
7580 + name = "quasi-basics"
7581 + version = "0.83.0"
7582 +
7583 + [[patch.unused]]
7584 + name = "quasi-http"
7585 + version = "0.83.0"
7586 +
7587 + [[patch.unused]]
7588 + name = "quasi-notifs"
7589 + version = "0.83.0"
7590 +
7591 + [[patch.unused]]
7592 + name = "quasi-store"
7593 + version = "0.1.0"
7594 +
7595 + [[patch.unused]]
7596 + name = "quasi-tauri"
7597 + version = "0.83.0"
7598 +
7599 + [[patch.unused]]
7600 + name = "quasi-webview"
7601 + version = "0.83.0"
M Cargo.toml +2 -2
@@ -26,8 +26,8 @@
26 26 # The described screens, which are audiofiles-browser's screens. By git URL with
27 27 # a version requirement, per the tree's rule for cross-repo deps. They were
28 28 # behind a `quasi` feature until 2026-08-25, when the last flip landed.
29 - quasi-router = { git = "https://makenot.work/git/max/quasi.git", version = "0.82" }
30 - quasi-immediate = { git = "https://makenot.work/git/max/quasi.git", version = "0.82" }
29 + quasi-router = { git = "https://makenot.work/git/max/quasi.git", version = "0.83" }
30 + quasi-immediate = { git = "https://makenot.work/git/max/quasi.git", version = "0.83" }
31 31 egui = { version = "0.35", default-features = false, features = ["default_fonts"] }
32 32 egui_extras = { version = "0.35", default-features = false }
33 33 eframe = { version = "0.35", default-features = false, features = ["default_fonts", "glow"] }
@@ -41,7 +41,7 @@
41 41 //! [`Outcome::File`]: quasi_router::Outcome::File
42 42 //! [`Outcome::Locate`]: quasi_router::Outcome::Locate
43 43
44 - use quasi_router::layout::{FieldKind, Notice, Tone};
44 + use quasi_router::layout::{FieldKind, Tone};
45 45 use quasi_router::{
46 46 Accepted, Act, Action, Field, Locating, Node, Request, Response, RouteError, Router, Slot,
47 47 Sought,
@@ -182,11 +182,10 @@
182 182 )));
183 183
184 184 if let Some(failed) = mirror.failed {
185 - body = body.with(Node::Notice {
186 - kind: Notice::Banner,
187 - tone: Tone::Warning,
188 - text: format!("Mirror setting not saved: {failed}. It applies to this session only."),
189 - });
185 + body = body.with(Node::banner(
186 + Tone::Warning,
187 + format!("Mirror setting not saved: {failed}. It applies to this session only."),
188 + ));
190 189 }
191 190 body
192 191 }
@@ -63,7 +63,7 @@
63 63 //! Three consumers, three members, one gap. Said as prose here for the same
64 64 //! reason and with the same complaint.
65 65
66 - use quasi_router::layout::{FieldKind, Notice, Tone};
66 + use quasi_router::layout::{FieldKind, Tone};
67 67 use quasi_router::{
68 68 Act, Action, Cell, Cells, Choice, Column, Field, Node, Outcome, RegionKind, Request, Response,
69 69 Rest, RouteError, Router, Screen, Slot, Tag,
@@ -438,11 +438,7 @@
438 438 // rather than drawn as an empty table, which would read as "this renames
439 439 // nothing".
440 440 Err(why) => {
441 - return Node::Notice {
442 - kind: Notice::Banner,
443 - tone: Tone::Danger,
444 - text: why,
445 - };
441 + return Node::banner(Tone::Danger, why);
446 442 }
447 443 };
448 444 if previews.is_empty() {
@@ -119,7 +119,7 @@
119 119 //! [`Outcome::File`]: quasi_router::Outcome::File
120 120 //! [`Repeat`]: quasi_router::Repeat
121 121
122 - use quasi_router::layout::{FieldKind, Notice, Readiness as Ready, Tone};
122 + use quasi_router::layout::{FieldKind, Readiness as Ready, Tone};
123 123 use quasi_router::{
124 124 Accepted, Act, Action, Choice, Field, Locating, Node, RegionKind, Request, Response,
125 125 RouteError, Router, Row, Screen, Slot, Sought, Tag,
@@ -691,11 +691,7 @@
691 691 act: None,
692 692 });
693 693 } else if let Some(why) = state.classifier.failed() {
694 - body = body.with(Node::Notice {
695 - kind: Notice::Banner,
696 - tone: Tone::Warning,
697 - text: why,
698 - });
694 + body = body.with(Node::banner(Tone::Warning, why));
699 695 }
700 696
701 697 body = rules(body, state);
@@ -68,7 +68,7 @@
68 68 //! wrong layer: an intent is for the app's own UI state, and a clipboard is the
69 69 //! *system's*. Written down rather than worked around quietly.
70 70
71 - use quasi_router::layout::{FieldKind, Notice, Tone};
71 + use quasi_router::layout::{FieldKind, Tone};
72 72 use quasi_router::{
73 73 Act, Action, Field, Node, RegionKind, Request, Response, RouteError, Router, Screen, Slot, Tag,
74 74 };
@@ -448,18 +448,10 @@
448 448 // The preconditions, as prose beside the controls they are about. The
449 449 // finding is that this belongs on the control.
450 450 if !sample.has_spectral {
451 - body = body.with(Node::Notice {
452 - kind: Notice::Banner,
453 - tone: Tone::Info,
454 - text: SPECTRAL.to_owned(),
455 - });
451 + body = body.with(Node::banner(Tone::Info, SPECTRAL));
456 452 }
457 453 if !sample.has_fingerprint {
458 - body = body.with(Node::Notice {
459 - kind: Notice::Banner,
460 - tone: Tone::Info,
461 - text: FINGERPRINT.to_owned(),
462 - });
454 + body = body.with(Node::banner(Tone::Info, FINGERPRINT));
463 455 }
464 456 body
465 457 }
@@ -96,7 +96,7 @@
96 96 //! Described as `Act::disabled` on what an edit would start, which says the
97 97 //! same thing without naming a colour.
98 98
99 - use quasi_router::layout::{FieldKind, Notice, Tone};
99 + use quasi_router::layout::{FieldKind, Tone};
100 100 use quasi_router::{
101 101 Act, Action, Choice, Field, Figure, Node, RegionKind, Request, Response, RouteError, Router,
102 102 Screen, Slot,
@@ -255,11 +255,7 @@
255 255
256 256 if sample.working {
257 257 body = body
258 - .with(Node::Notice {
259 - kind: Notice::Banner,
260 - tone: Tone::Info,
261 - text: "Applying edit...".to_owned(),
262 - })
258 + .with(Node::banner(Tone::Info, "Applying edit..."))
263 259 .with(Node::Act(Act::new("Cancel", Action::post("/edit/cancel"))));
264 260 }
265 261
@@ -362,11 +358,10 @@
362 358 if predicted <= 0.0 {
363 359 return Node::text(format!("Peak: {peak:.1} dB -> {predicted:.1} dB"));
364 360 }
365 - Node::Notice {
366 - kind: Notice::Banner,
367 - tone: Tone::Danger,
368 - text: format!("Peak: {peak:.1} dB -> {predicted:.1} dB (clips!)"),
369 - }
361 + Node::banner(
362 + Tone::Danger,
363 + format!("Peak: {peak:.1} dB -> {predicted:.1} dB (clips!)"),
364 + )
370 365 }
371 366
372 367 /// Reverse and fade.
@@ -456,11 +451,7 @@
456 451 .with(Node::Field(Box::new(field)));
457 452
458 453 if sample.result.as_deref() == Some(EditResultMode::Replace.as_value()) {
459 - body = body.with(Node::Notice {
460 - kind: Notice::Banner,
461 - tone: Tone::Warning,
462 - text: "Replace mode: the original is removed from this vault. Use Create sibling to keep both, or Undo below to revert.".to_owned(),
463 - });
454 + body = body.with(Node::banner(Tone::Warning, "Replace mode: the original is removed from this vault. Use Create sibling to keep both, or Undo below to revert."));
464 455 }
465 456
466 457 // The standing undo. See the module header: this is an act rather than
@@ -40,7 +40,7 @@
40 40 //! it, which is `524a63fe`'s argument and the third `ConfirmAction`-shaped thing
41 41 //! this port has replaced with a builder method.
42 42
43 - use quasi_router::layout::{Notice, Tone};
43 + use quasi_router::layout::Tone;
44 44 use quasi_router::{
45 45 Act, Action, Locating, Node, Outcome, RegionKind, Request, Response, RouteError, Router,
46 46 Screen, Slot,
@@ -80,16 +80,12 @@
80 80
81 81 let body = Slot::new(BODY, RegionKind::Pane)
82 82 .with(Node::page("Loose-files mode warning"))
83 - .with(Node::Notice {
84 - kind: Notice::Banner,
85 - tone: Tone::Warning,
86 - text: format!(
83 + .with(Node::banner(Tone::Warning, format!(
87 84 "{missing} sample{} in this vault {} missing source {}.",
88 85 if missing == 1 { "" } else { "s" },
89 86 if missing == 1 { "has a" } else { "have" },
90 87 if missing == 1 { "file" } else { "files" },
91 - ),
92 - })
88 + )))
93 89 .with(Node::text(
94 90 "The original files may have been moved or deleted. These samples cannot be played or exported until the files are restored.",
95 91 ))
@@ -63,7 +63,7 @@
63 63 //! [`Candidate::confident`](super::Candidate::confident) can do exactly that;
64 64 //! choosing the colour is not the description's.
65 65
66 - use quasi_router::layout::{Notice, Tone};
66 + use quasi_router::layout::Tone;
67 67 use quasi_router::{
68 68 Act, Action, Figure, Node, Prose, RegionKind, Request, Response, Rest, RouteError, Router, Row,
69 69 Screen, Slot,
@@ -439,11 +439,7 @@
439 439 // app reporting rather than the screen describing, which is the split
440 440 // `shell`'s status band already draws.
441 441 match &queued.said {
442 - Some(said) => band.with(Node::Notice {
443 - kind: Notice::Toast,
444 - tone: Tone::Success,
445 - text: said.clone(),
446 - }),
442 + Some(said) => band.with(Node::toast(Tone::Success, said.clone())),
447 443 None => band.with(Node::Figure(Figure::new(
448 444 queued.confident.to_string(),
449 445 "confident",
@@ -105,7 +105,7 @@
105 105 //! pass verifies the root is clean, so stopping defers the remainder rather than
106 106 //! abandoning it.
107 107
108 - use quasi_router::layout::{Notice, Priority, Tone};
108 + use quasi_router::layout::{Priority, Tone};
109 109 use quasi_router::{
110 110 Act, Action, Figure, Meter, Node, RegionKind, Request, Response, RouteError, Router, Screen,
111 111 Slot, Tag,
@@ -322,15 +322,14 @@
322 322 if missing == 0 {
323 323 return band;
324 324 }
325 - band.with(Node::Notice {
326 - kind: Notice::Banner,
327 - tone: Tone::Warning,
328 - text: format!(
325 + band.with(Node::banner(
326 + Tone::Warning,
327 + format!(
329 328 "{missing} sample{} cannot find {} file.",
330 329 if missing == 1 { "" } else { "s" },
331 330 if missing == 1 { "its" } else { "their" },
332 331 ),
333 - })
332 + ))
334 333 .with(Node::Act(Act::new(
335 334 "What is missing",
336 335 Action::get("/library/loose-files"),
@@ -346,14 +345,13 @@
346 345 /// and is what `Tone` carries; the second half is renderer policy and is gone.
347 346 fn saying(band: Slot, state: &Panels<'_>) -> Slot {
348 347 if let Some((text, saying)) = state.shell.status() {
349 - return band.with(Node::Notice {
350 - kind: Notice::Toast,
351 - tone: match saying {
348 + return band.with(Node::toast(
349 + match saying {
352 350 Saying::Failed => Tone::Danger,
353 351 Saying::Ordinary => Tone::Neutral,
354 352 },
355 353 text,
356 - });
354 + ));
357 355 }
358 356 if state.shell.hinting() {
359 357 return band
@@ -330,11 +330,7 @@
330 330 // them. `Node::Notice` carries the tone and the text; what to do about it is
331 331 // two controls, and Retry is only offered where retrying means anything.
332 332 if let Some(error) = &status.last_error {
333 - body = body.with(Node::Notice {
334 - kind: quasi_router::layout::Notice::Banner,
335 - tone: Tone::Danger,
336 - text: error.clone(),
337 - });
333 + body = body.with(Node::banner(Tone::Danger, error.clone()));
338 334 if matches!(status.state, State::Ready | State::Syncing) {
339 335 body = body.with(Node::Act(Act::new("Retry", Action::post("/sync/now"))));
340 336 }
@@ -525,15 +521,14 @@
525 521 // first news of a full cap is a failed upload - the 402 from
526 522 // `routes/synckit/blobs.rs`, which the user meets as a sync that broke.
527 523 if let Some(warning) = cap_warning(sync, sub, pricing) {
528 - slot = slot.with(Node::Notice {
529 - kind: quasi_router::layout::Notice::Banner,
530 - tone: if sub.used_bytes >= sub.limit_bytes {
524 + slot = slot.with(Node::banner(
525 + if sub.used_bytes >= sub.limit_bytes {
531 526 Tone::Danger
532 527 } else {
533 528 Tone::Warning
534 529 },
535 - text: warning,
536 - });
530 + warning,
531 + ));
537 532 }
538 533
539 534 if let Some(pending) = sub.pending_limit_bytes {