max / goingson
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01AAbx8dxVmuVeyKbKoRrUL2
6 files changed,
+182 insertions,
-96 deletions
| @@ -1387,7 +1387,7 @@ | |||
| 1387 | 1387 | "libc", | |
| 1388 | 1388 | "option-ext", | |
| 1389 | 1389 | "redox_users", | |
| 1390 | - | "windows-sys 0.59.0", | |
| 1390 | + | "windows-sys 0.61.2", | |
| 1391 | 1391 | ] | |
| 1392 | 1392 | ||
| 1393 | 1393 | [[package]] | |
| @@ -1628,7 +1628,7 @@ | |||
| 1628 | 1628 | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" | |
| 1629 | 1629 | dependencies = [ | |
| 1630 | 1630 | "libc", | |
| 1631 | - | "windows-sys 0.59.0", | |
| 1631 | + | "windows-sys 0.61.2", | |
| 1632 | 1632 | ] | |
| 1633 | 1633 | ||
| 1634 | 1634 | [[package]] | |
| @@ -3433,9 +3433,9 @@ | |||
| 3433 | 3433 | ||
| 3434 | 3434 | [[package]] | |
| 3435 | 3435 | name = "makeover" | |
| 3436 | - | version = "3.0.0" | |
| 3436 | + | version = "3.1.0" | |
| 3437 | 3437 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3438 | - | checksum = "3e6da78c376074e48eb2324394004afc847e674e05ed2ea4bec1331073990b3f" | |
| 3438 | + | checksum = "d0fba4022268d1d73423bb16126dfdaa714a63bd4ff24f826dd645255eb0aad4" | |
| 3439 | 3439 | dependencies = [ | |
| 3440 | 3440 | "include_dir", | |
| 3441 | 3441 | "serde", | |
| @@ -3444,9 +3444,9 @@ | |||
| 3444 | 3444 | ||
| 3445 | 3445 | [[package]] | |
| 3446 | 3446 | name = "makeover-build" | |
| 3447 | - | version = "0.57.0" | |
| 3447 | + | version = "0.58.0" | |
| 3448 | 3448 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3449 | - | checksum = "0f8d7b3e487f34397f239cd34820a6f0a02fdd57bdd8c72af851c4e0dd8810ef" | |
| 3449 | + | checksum = "2d1bd0bb12d887506c9366326d6e3d0fe32226c8f3e92c1972b0d12ad317aa3a" | |
| 3450 | 3450 | dependencies = [ | |
| 3451 | 3451 | "makeover", | |
| 3452 | 3452 | "makeover-geometry", | |
| @@ -3462,9 +3462,9 @@ | |||
| 3462 | 3462 | ||
| 3463 | 3463 | [[package]] | |
| 3464 | 3464 | name = "makeover-layout" | |
| 3465 | - | version = "0.37.0" | |
| 3465 | + | version = "0.38.0" | |
| 3466 | 3466 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3467 | - | checksum = "369bc3c3e0ee91d24b3552f48f1ecb1764bca953725fb817df33b2dc80f2d34f" | |
| 3467 | + | checksum = "73ad4a03ed87470b838bd84be586739682f0a1210db1d7ad4a0146cb9ef7734b" | |
| 3468 | 3468 | ||
| 3469 | 3469 | [[package]] | |
| 3470 | 3470 | name = "makeover-timing" | |
| @@ -3477,9 +3477,9 @@ | |||
| 3477 | 3477 | ||
| 3478 | 3478 | [[package]] | |
| 3479 | 3479 | name = "makeover-touch" | |
| 3480 | - | version = "0.27.0" | |
| 3480 | + | version = "0.28.0" | |
| 3481 | 3481 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3482 | - | checksum = "b180e22917189e05d291bfec0e930b5e6c2952dc7bf6e2f7a2262b903431cc72" | |
| 3482 | + | checksum = "10693ddd1a7c5aad0289bcee2bb02c25cef10e292820a60a2813d21411217a83" | |
| 3483 | 3483 | dependencies = [ | |
| 3484 | 3484 | "makeover-geometry", | |
| 3485 | 3485 | "makeover-layout", | |
| @@ -3487,9 +3487,9 @@ | |||
| 3487 | 3487 | ||
| 3488 | 3488 | [[package]] | |
| 3489 | 3489 | name = "makeover-tui" | |
| 3490 | - | version = "0.37.0" | |
| 3490 | + | version = "0.38.0" | |
| 3491 | 3491 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3492 | - | checksum = "b5e2ea93b84f2cb85fde760d360ff4c095320db242a0d0c08cbaf2f396a8de8e" | |
| 3492 | + | checksum = "0b2702bf3d12d4f978607a6209d5ebc9786e46adb13b2ee2c704881b391ec1bd" | |
| 3493 | 3493 | dependencies = [ | |
| 3494 | 3494 | "makeover", | |
| 3495 | 3495 | "makeover-layout", | |
| @@ -3499,9 +3499,9 @@ | |||
| 3499 | 3499 | ||
| 3500 | 3500 | [[package]] | |
| 3501 | 3501 | name = "makeover-webview" | |
| 3502 | - | version = "0.64.0" | |
| 3502 | + | version = "0.65.0" | |
| 3503 | 3503 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3504 | - | checksum = "6b91db6e169bc8b7c7225e17b6bc83f9c6f1879eb3bc6f1554e96cb621c84e5c" | |
| 3504 | + | checksum = "899e27bcbcf2d8de779c0404ca5e1e354d9ff0fa9d834f1855c5202d0271c80f" | |
| 3505 | 3505 | dependencies = [ | |
| 3506 | 3506 | "makeover-geometry", | |
| 3507 | 3507 | "makeover-layout", | |
| @@ -3783,7 +3783,7 @@ | |||
| 3783 | 3783 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3784 | 3784 | checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" | |
| 3785 | 3785 | dependencies = [ | |
| 3786 | - | "windows-sys 0.59.0", | |
| 3786 | + | "windows-sys 0.61.2", | |
| 3787 | 3787 | ] | |
| 3788 | 3788 | ||
| 3789 | 3789 | [[package]] | |
| @@ -3891,7 +3891,7 @@ | |||
| 3891 | 3891 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 3892 | 3892 | checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" | |
| 3893 | 3893 | dependencies = [ | |
| 3894 | - | "proc-macro-crate 1.3.1", | |
| 3894 | + | "proc-macro-crate 3.5.0", | |
| 3895 | 3895 | "proc-macro2", | |
| 3896 | 3896 | "quote", | |
| 3897 | 3897 | "syn 2.0.119", | |
| @@ -4717,7 +4717,7 @@ | |||
| 4717 | 4717 | ||
| 4718 | 4718 | [[package]] | |
| 4719 | 4719 | name = "quasi-http" | |
| 4720 | - | version = "0.74.0" | |
| 4720 | + | version = "0.75.0" | |
| 4721 | 4721 | dependencies = [ | |
| 4722 | 4722 | "form_urlencoded", | |
| 4723 | 4723 | "http", | |
| @@ -4726,7 +4726,7 @@ | |||
| 4726 | 4726 | ||
| 4727 | 4727 | [[package]] | |
| 4728 | 4728 | name = "quasi-notifs" | |
| 4729 | - | version = "0.74.0" | |
| 4729 | + | version = "0.75.0" | |
| 4730 | 4730 | dependencies = [ | |
| 4731 | 4731 | "quasi-router", | |
| 4732 | 4732 | "synckit-config", | |
| @@ -4734,14 +4734,14 @@ | |||
| 4734 | 4734 | ||
| 4735 | 4735 | [[package]] | |
| 4736 | 4736 | name = "quasi-router" | |
| 4737 | - | version = "0.74.0" | |
| 4737 | + | version = "0.75.0" | |
| 4738 | 4738 | dependencies = [ | |
| 4739 | 4739 | "makeover-layout", | |
| 4740 | 4740 | ] | |
| 4741 | 4741 | ||
| 4742 | 4742 | [[package]] | |
| 4743 | 4743 | name = "quasi-tauri" | |
| 4744 | - | version = "0.74.0" | |
| 4744 | + | version = "0.75.0" | |
| 4745 | 4745 | dependencies = [ | |
| 4746 | 4746 | "http", | |
| 4747 | 4747 | "quasi-http", | |
| @@ -4751,7 +4751,7 @@ | |||
| 4751 | 4751 | ||
| 4752 | 4752 | [[package]] | |
| 4753 | 4753 | name = "quasi-type" | |
| 4754 | - | version = "0.1.1" | |
| 4754 | + | version = "0.1.2" | |
| 4755 | 4755 | dependencies = [ | |
| 4756 | 4756 | "brotli", | |
| 4757 | 4757 | "font-types", | |
| @@ -4767,7 +4767,7 @@ | |||
| 4767 | 4767 | ||
| 4768 | 4768 | [[package]] | |
| 4769 | 4769 | name = "quasi-webview" | |
| 4770 | - | version = "0.74.0" | |
| 4770 | + | version = "0.75.0" | |
| 4771 | 4771 | dependencies = [ | |
| 4772 | 4772 | "docengine", | |
| 4773 | 4773 | "makeover-layout", | |
| @@ -5206,7 +5206,7 @@ | |||
| 5206 | 5206 | "errno", | |
| 5207 | 5207 | "libc", | |
| 5208 | 5208 | "linux-raw-sys", | |
| 5209 | - | "windows-sys 0.59.0", | |
| 5209 | + | "windows-sys 0.61.2", | |
| 5210 | 5210 | ] | |
| 5211 | 5211 | ||
| 5212 | 5212 | [[package]] | |
| @@ -5263,7 +5263,7 @@ | |||
| 5263 | 5263 | "security-framework", | |
| 5264 | 5264 | "security-framework-sys", | |
| 5265 | 5265 | "webpki-root-certs", | |
| 5266 | - | "windows-sys 0.59.0", | |
| 5266 | + | "windows-sys 0.61.2", | |
| 5267 | 5267 | ] | |
| 5268 | 5268 | ||
| 5269 | 5269 | [[package]] | |
| @@ -6575,10 +6575,10 @@ | |||
| 6575 | 6575 | checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" | |
| 6576 | 6576 | dependencies = [ | |
| 6577 | 6577 | "fastrand", | |
| 6578 | - | "getrandom 0.3.4", | |
| 6578 | + | "getrandom 0.4.3", | |
| 6579 | 6579 | "once_cell", | |
| 6580 | 6580 | "rustix", | |
| 6581 | - | "windows-sys 0.59.0", | |
| 6581 | + | "windows-sys 0.61.2", | |
| 6582 | 6582 | ] | |
| 6583 | 6583 | ||
| 6584 | 6584 | [[package]] | |
| @@ -7647,7 +7647,7 @@ | |||
| 7647 | 7647 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 7648 | 7648 | checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" | |
| 7649 | 7649 | dependencies = [ | |
| 7650 | - | "windows-sys 0.59.0", | |
| 7650 | + | "windows-sys 0.61.2", | |
| 7651 | 7651 | ] | |
| 7652 | 7652 | ||
| 7653 | 7653 | [[package]] | |
| @@ -8505,21 +8505,21 @@ | |||
| 8505 | 8505 | ] | |
| 8506 | 8506 | ||
| 8507 | 8507 | [[patch.unused]] | |
| 8508 | - | name = "quasi-axum" | |
| 8509 | - | version = "0.74.0" | |
| 8510 | - | ||
| 8511 | - | [[patch.unused]] | |
| 8512 | - | name = "quasi-basics" | |
| 8513 | - | version = "0.74.0" | |
| 8514 | - | ||
| 8515 | - | [[patch.unused]] | |
| 8516 | - | name = "quasi-immediate" | |
| 8517 | - | version = "0.74.0" | |
| 8518 | - | ||
| 8519 | - | [[patch.unused]] | |
| 8520 | - | name = "quasi-store" | |
| 8508 | + | name = "ops-status" | |
| 8521 | 8509 | version = "0.1.0" | |
| 8522 | 8510 | ||
| 8523 | 8511 | [[patch.unused]] | |
| 8524 | - | name = "ops-status" | |
| 8512 | + | name = "quasi-axum" | |
| 8513 | + | version = "0.75.0" | |
| 8514 | + | ||
| 8515 | + | [[patch.unused]] | |
| 8516 | + | name = "quasi-basics" | |
| 8517 | + | version = "0.75.0" | |
| 8518 | + | ||
| 8519 | + | [[patch.unused]] | |
| 8520 | + | name = "quasi-immediate" | |
| 8521 | + | version = "0.75.0" | |
| 8522 | + | ||
| 8523 | + | [[patch.unused]] | |
| 8524 | + | name = "quasi-store" | |
| 8525 | 8525 | version = "0.1.0" |
| @@ -104,8 +104,8 @@ | |||
| 104 | 104 | # Filesystem watching (db change notifications) | |
| 105 | 105 | notify = "8.2" | |
| 106 | 106 | notify-debouncer-mini = "0.7" | |
| 107 | - | makeover = "3.0" | |
| 108 | - | makeover-tui = "0.37.0" | |
| 107 | + | makeover = "3.1" | |
| 108 | + | makeover-tui = "0.38.0" | |
| 109 | 109 | toml = "1.1" | |
| 110 | 110 | ||
| 111 | 111 | # Enums |
| @@ -18,7 +18,7 @@ | |||
| 18 | 18 | # Materialises all three generated files: themes/, geometry.css, layout.css, | |
| 19 | 19 | # and holds both drift checks this build.rs used to carry itself: touch density | |
| 20 | 20 | # as of 0.17, breakpoints as of 0.18. | |
| 21 | - | makeover-build = "0.57.0" | |
| 21 | + | makeover-build = "0.58.0" | |
| 22 | 22 | # Cuts the two house faces into frontend/fonts/. A git dependency because | |
| 23 | 23 | # quasi-type is `publish = false`, which is also why makeover-build cannot do | |
| 24 | 24 | # this and only emits the CSS that fetches the result. The rev is the one | |
| @@ -28,15 +28,15 @@ | |||
| 28 | 28 | # The table CSS is generated here too: the columns are this app's, so the | |
| 29 | 29 | # shared helper cannot know them, but the tracks and the narrowing rules come | |
| 30 | 30 | # from the description rather than from hand-written nth-child cuts. | |
| 31 | - | makeover-webview = "0.64.0" | |
| 32 | - | makeover-layout = "0.37.0" | |
| 31 | + | makeover-webview = "0.65.0" | |
| 32 | + | makeover-layout = "0.38.0" | |
| 33 | 33 | # Width. Direct rather than through makeover-webview, because the narrow table | |
| 34 | 34 | # pass keys off SizeClass::Compact and a boundary reached transitively is a | |
| 35 | 35 | # boundary nobody pinned. | |
| 36 | 36 | makeover-geometry = "0.7" | |
| 37 | 37 | # Only to write its selection and clock scripts into the frontend. Those two are | |
| 38 | 38 | # the whole of `frontend/js/` since the swap. | |
| 39 | - | quasi-webview = { git = "https://makenot.work/git/max/quasi.git", version = "0.74" } | |
| 39 | + | quasi-webview = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 40 | 40 | ||
| 41 | 41 | [dependencies] | |
| 42 | 42 | goingson-core = { workspace = true } | |
| @@ -54,10 +54,10 @@ | |||
| 54 | 54 | # dependency's source even with the feature off -- so a public clone could not | |
| 55 | 55 | # build at all against a URL it could not reach. quasi went public that day and | |
| 56 | 56 | # the URL moved here with it. | |
| 57 | - | quasi-router = { git = "https://makenot.work/git/max/quasi.git", version = "0.74" } | |
| 58 | - | quasi-http = { git = "https://makenot.work/git/max/quasi.git", version = "0.74" } | |
| 59 | - | quasi-webview = { git = "https://makenot.work/git/max/quasi.git", version = "0.74" } | |
| 60 | - | quasi-tauri = { git = "https://makenot.work/git/max/quasi.git", version = "0.74" } | |
| 57 | + | quasi-router = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 58 | + | quasi-http = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 59 | + | quasi-webview = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 60 | + | quasi-tauri = { git = "https://makenot.work/git/max/quasi.git", version = "0.75" } | |
| 61 | 61 | ||
| 62 | 62 | # The notification declaration, and it is NOT behind the `quasi` feature: the | |
| 63 | 63 | # watcher is desktop app behaviour rather than a described screen, and the | |
| @@ -74,7 +74,7 @@ | |||
| 74 | 74 | # before it has ported its screens cannot take it. `notifications::Notifier` is | |
| 75 | 75 | # the eight lines meanwhile, and it goes when the flip makes quasi-router | |
| 76 | 76 | # unconditional here. | |
| 77 | - | quasi-notifs = { git = "https://makenot.work/git/max/quasi.git", version = "0.74", features = ["synckit", "describe"] } | |
| 77 | + | quasi-notifs = { git = "https://makenot.work/git/max/quasi.git", version = "0.75", features = ["synckit", "describe"] } | |
| 78 | 78 | ||
| 79 | 79 | # Tauri | |
| 80 | 80 | tauri = { workspace = true, features = ["image-png", "devtools"] } | |
| @@ -139,8 +139,8 @@ | |||
| 139 | 139 | # The forms emitter and the description it renders. Runtime deps, not build: | |
| 140 | 140 | # form markup depends on the value, the error and the options at the moment a | |
| 141 | 141 | # modal opens, so it cannot be materialised the way the stylesheet is. | |
| 142 | - | makeover-webview = "0.64.0" | |
| 143 | - | makeover-layout = "0.37.0" | |
| 142 | + | makeover-webview = "0.65.0" | |
| 143 | + | makeover-layout = "0.38.0" | |
| 144 | 144 | # Browser opening | |
| 145 | 145 | open = { workspace = true } | |
| 146 | 146 |
| @@ -194,6 +194,13 @@ | |||
| 194 | 194 | // places. | |
| 195 | 195 | placeholder: spec.placeholder.as_deref(), | |
| 196 | 196 | options: &choices, | |
| 197 | + | // makeover-layout 0.38.0's theme picker, and the same position as | |
| 198 | + | // the file members below: this JS form spec cannot name a theme | |
| 199 | + | // kind, so it never carries themes. The described settings screen | |
| 200 | + | // is where the picker lives (`quasi::settings`), and it builds one | |
| 201 | + | // out of what the host resolved rather than out of a form spec. | |
| 202 | + | themes: &[], | |
| 203 | + | follows: None, | |
| 197 | 204 | // makeover-layout 0.31.0's file members, and the same position: | |
| 198 | 205 | // the JS form spec carries no accept list and no multiplicity, so | |
| 199 | 206 | // an empty list is what this app actually says. It means any file, |
| @@ -120,8 +120,8 @@ | |||
| 120 | 120 | use std::collections::HashMap; | |
| 121 | 121 | ||
| 122 | 122 | use quasi_notifs::pane; | |
| 123 | - | use quasi_router::layout::Heading; | |
| 124 | - | use quasi_router::screen::{Choice, Field, Row}; | |
| 123 | + | use quasi_router::layout::{Contrast, Heading, ThemeVariant}; | |
| 124 | + | use quasi_router::screen::{Choice, Field, Row, ThemeChoice}; | |
| 125 | 125 | use quasi_router::{Action, Node, RegionKind, Response, RouteError, Router, Screen, Slot}; | |
| 126 | 126 | ||
| 127 | 127 | use crate::notifs::NOTIFS; | |
| @@ -296,36 +296,82 @@ | |||
| 296 | 296 | ||
| 297 | 297 | /// The themes on offer. | |
| 298 | 298 | /// | |
| 299 | - | /// # The first finding | |
| 299 | + | /// # The first finding, and it is closed | |
| 300 | 300 | /// | |
| 301 | - | /// **A set of choices cannot be grouped.** `renderAppearance` puts the themes in | |
| 302 | - | /// four `<optgroup>`s — System, Light Themes, Dark Themes, High Contrast — and | |
| 303 | - | /// [`Choice`] is a value and a label. A flat list of twenty themes with no | |
| 304 | - | /// grouping is a worse control than the one it replaces, so the variant goes | |
| 305 | - | /// into the label, which keeps the fact and loses the structure. | |
| 301 | + | /// This section used to record that a set of choices cannot be grouped: the | |
| 302 | + | /// shipped JS put the themes in four `<optgroup>`s, [`Choice`] is a value and a | |
| 303 | + | /// label, so the variant went into the label and the structure was lost. The | |
| 304 | + | /// note filed alongside it proposed a group on the choice, "the same shape in | |
| 305 | + | /// every app that has ever filled a select from more than one source". | |
| 306 | 306 | /// | |
| 307 | - | /// Small, and filed rather than worked around further: the honest fix is a | |
| 308 | - | /// group on the choice, and it is the same shape in every app that has ever | |
| 309 | - | /// filled a select from more than one source. | |
| 307 | + | /// Both halves were wrong, and the measurement is what says so. Grouping across | |
| 308 | + | /// the tree was counted on 2026-08-28: `optgroup` appears at exactly one live | |
| 309 | + | /// site, in the one app not yet ported, and the non-theme grouping count is | |
| 310 | + | /// zero. So the recurring thing was never option lists that group. It was this | |
| 311 | + | /// picker, hand-written in three apps. | |
| 310 | 312 | /// | |
| 311 | - | /// The list itself is read through [`AppState::theme_dirs`], which is the whole | |
| 312 | - | /// reason this section exists at all — see the module header. | |
| 313 | - | fn theme_choices(state: &AppState) -> Vec<Choice> { | |
| 314 | - | let mut choices = vec![Choice::new("system", "Follow System")]; | |
| 315 | - | for theme in makeover::list_themes_from_dirs(&state.theme_dirs) { | |
| 316 | - | let variant = match theme.variant.as_str() { | |
| 317 | - | "high-contrast" => "High Contrast", | |
| 318 | - | "light" => "Light", | |
| 319 | - | // makeover's variant is a string and dark is what everything else | |
| 320 | - | // is, which is the same fallback `getThemesByType` makes. | |
| 321 | - | _ => "Dark", | |
| 322 | - | }; | |
| 323 | - | choices.push(Choice::new( | |
| 324 | - | &theme.id, | |
| 325 | - | format!("{} ({variant})", theme.name), | |
| 326 | - | )); | |
| 313 | + | /// What landed instead is [`Field::theme`] (makeover-layout 0.38.0, ruled by | |
| 314 | + | /// Max on `70028e00`): a described theme picker whose entries carry their | |
| 315 | + | /// variant and their measured contrast tier as values rather than as prose. The | |
| 316 | + | /// grouping comes back, and the tier arrives with it — a fact this app never | |
| 317 | + | /// had, because deriving it means resolving every theme's colours and this | |
| 318 | + | /// screen only ever had the names. | |
| 319 | + | /// | |
| 320 | + | /// # Nothing here sorts, and nothing here groups | |
| 321 | + | /// | |
| 322 | + | /// [`makeover::theme_options`] returns the list already ordered by variant and | |
| 323 | + | /// then by measured contrast, which is the order the renderer reads the groups | |
| 324 | + | /// out of. Re-sorting here would be this app deciding a question it cannot see | |
| 325 | + | /// the inputs to, and it is the duplication that put three different orders in | |
| 326 | + | /// three apps in the first place. | |
| 327 | + | /// | |
| 328 | + | /// The list is read through [`AppState::theme_dirs`], which is the whole reason | |
| 329 | + | /// this section exists at all — see the module header. | |
| 330 | + | fn theme_field(state: &AppState, config: &HashMap<String, String>) -> Field { | |
| 331 | + | let themes = makeover::theme_options(&state.theme_dirs) | |
| 332 | + | .into_iter() | |
| 333 | + | .map(|theme| { | |
| 334 | + | ThemeChoice::new( | |
| 335 | + | theme.id, | |
| 336 | + | theme.name, | |
| 337 | + | variant_of(theme.variant), | |
| 338 | + | tier_of(theme.contrast), | |
| 339 | + | ) | |
| 340 | + | }) | |
| 341 | + | .collect(); | |
| 342 | + | ||
| 343 | + | // `makeover::FOLLOW` rather than a literal: the sentinel this app stores is | |
| 344 | + | // the one the crate that resolves it reads back, and spelling it here is | |
| 345 | + | // how the two drift. | |
| 346 | + | Field::theme("theme", "Theme", themes) | |
| 347 | + | .following(Choice::new(makeover::FOLLOW, "Follow System")) | |
| 348 | + | .value(value_of(config, "theme")) | |
| 349 | + | } | |
| 350 | + | ||
| 351 | + | /// `makeover`'s variant as the description layer's own. | |
| 352 | + | /// | |
| 353 | + | /// The seam the layering costs, and it is three arms. `makeover-layout` takes | |
| 354 | + | /// no dependencies by charter, so it cannot name `makeover::Variant`, and a | |
| 355 | + | /// renderer that groups a picker needs the groups as values. Exhaustive rather | |
| 356 | + | /// than wildcarded on purpose: a fourth ambient mode should stop the build here | |
| 357 | + | /// rather than quietly file itself under dark. | |
| 358 | + | fn variant_of(variant: makeover::Variant) -> ThemeVariant { | |
| 359 | + | match variant { | |
| 360 | + | makeover::Variant::Light => ThemeVariant::Light, | |
| 361 | + | makeover::Variant::Dark => ThemeVariant::Dark, | |
| 362 | + | makeover::Variant::HighContrast => ThemeVariant::HighContrast, | |
| 363 | + | } | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | /// `makeover`'s measured tier as the description layer's own. | |
| 367 | + | /// | |
| 368 | + | /// [`variant_of`]'s twin, for its reason and with its exhaustiveness. | |
| 369 | + | fn tier_of(tier: makeover::ContrastTier) -> Contrast { | |
| 370 | + | match tier { | |
| 371 | + | makeover::ContrastTier::Low => Contrast::Low, | |
| 372 | + | makeover::ContrastTier::Standard => Contrast::Standard, | |
| 373 | + | makeover::ContrastTier::High => Contrast::High, | |
| 327 | 374 | } | |
| 328 | - | choices | |
| 329 | 375 | } | |
| 330 | 376 | ||
| 331 | 377 | /// Appearance. | |
| @@ -358,12 +404,10 @@ | |||
| 358 | 404 | fn appearance(state: &AppState, config: &HashMap<String, String>) -> Vec<Node> { | |
| 359 | 405 | vec![ | |
| 360 | 406 | Node::section("Appearance"), | |
| 361 | - | setting( | |
| 362 | - | choice_field(config, "theme", "Theme", theme_choices(state)).hint( | |
| 363 | - | "Follow System switches with the desktop, straight away. A \ | |
| 407 | + | setting(theme_field(state, config).hint( | |
| 408 | + | "Follow System switches with the desktop, straight away. A \ | |
| 364 | 409 | named theme takes effect the next time GoingsOn starts.", | |
| 365 | - | ), | |
| 366 | - | ), | |
| 410 | + | )), | |
| 367 | 411 | ] | |
| 368 | 412 | } | |
| 369 | 413 |
| @@ -299,20 +299,55 @@ | |||
| 299 | 299 | } | |
| 300 | 300 | ||
| 301 | 301 | #[tokio::test] | |
| 302 | - | async fn the_theme_list_comes_from_the_state_and_carries_its_variant_in_the_label() { | |
| 303 | - | // The first finding, asserted as the loss it is. The JS groups the themes | |
| 304 | - | // in four optgroups; Choice is a value and a label, so the variant goes | |
| 305 | - | // into the label - the fact kept, the structure gone. | |
| 302 | + | async fn the_theme_list_comes_from_the_state_and_keeps_its_grouping() { | |
| 303 | + | // The first finding, closed. This asserted the loss: the shipped JS grouped | |
| 304 | + | // the themes in four optgroups, `Choice` is a value and a label, so the | |
| 305 | + | // variant went into the label and the structure was gone - and the old | |
| 306 | + | // assertion was `!page.contains("optgroup")`. | |
| 306 | 307 | // | |
| 307 | - | // It is also the assertion that the host fact reached the handler at all: | |
| 308 | - | // the search path is built from an AppHandle the handler never sees, and | |
| 308 | + | // makeover-layout 0.38.0's `Field::theme` is what gave it back. The | |
| 309 | + | // grouping is a described fact now rather than markup this app writes, and | |
| 310 | + | // the contrast tier arrives with it, which this screen never had at all. | |
| 311 | + | // | |
| 312 | + | // It is still the assertion that the host fact reached the handler: the | |
| 313 | + | // search path is built from an AppHandle the handler never sees, and | |
| 309 | 314 | // AppState holds the result. | |
| 310 | 315 | let state = state().await; | |
| 311 | 316 | let page = html(get(&state, "/settings/appearance")); | |
| 312 | 317 | ||
| 313 | - | assert!(page.contains("Follow System")); | |
| 314 | - | assert!(page.contains("(Dark)") || page.contains("(Light)")); | |
| 315 | - | assert!(!page.contains("optgroup")); | |
| 318 | + | assert!(page.contains("Follow System"), "{page}"); | |
| 319 | + | assert!(page.contains("optgroup"), "{page}"); | |
| 320 | + | assert!(page.contains(r#"<optgroup label="Light""#), "{page}"); | |
| 321 | + | assert!(page.contains(r#"<optgroup label="Dark""#), "{page}"); | |
| 322 | + | // The variant is no longer bracketed onto the name; the badge is what sits | |
| 323 | + | // there now, and it is the fact the flat list could not carry. | |
| 324 | + | assert!(!page.contains("(Dark)"), "{page}"); | |
| 325 | + | assert!(page.contains("data-contrast="), "{page}"); | |
| 326 | + | } | |
| 327 | + | ||
| 328 | + | #[tokio::test] | |
| 329 | + | async fn the_theme_picker_orders_each_group_by_measured_contrast() { | |
| 330 | + | // The sort no app can do for itself: the tier comes off the resolved | |
| 331 | + | // colours, so a screen holding only names cannot produce this order. It is | |
| 332 | + | // `makeover::theme_options`' and this asserts it survived the description. | |
| 333 | + | let state = state().await; | |
| 334 | + | let page = html(get(&state, "/settings/appearance")); | |
| 335 | + | ||
| 336 | + | let light = page | |
| 337 | + | .find(r#"<optgroup label="Light""#) | |
| 338 | + | .expect("light group"); | |
| 339 | + | let dark = page.find(r#"<optgroup label="Dark""#).expect("dark group"); | |
| 340 | + | assert!(light < dark, "light comes first"); | |
| 341 | + | ||
| 342 | + | // Within the light group, AA before OK before low. | |
| 343 | + | let group = &page[light..dark]; | |
| 344 | + | let tier = |mark: &str| group.find(mark); | |
| 345 | + | if let (Some(aa), Some(low)) = ( | |
| 346 | + | tier(r#"data-contrast="high""#), | |
| 347 | + | tier(r#"data-contrast="low""#), | |
| 348 | + | ) { | |
| 349 | + | assert!(aa < low, "the most legible theme leads its group: {group}"); | |
| 350 | + | } | |
| 316 | 351 | } | |
| 317 | 352 | ||
| 318 | 353 | #[tokio::test] |