max / makenotwork
- Co-Authored-By
- Claude Opus 5 (1M context) <noreply@anthropic.com>
- Claude-Session
- https://claude.ai/code/session_01MptwXZ8k65v19rFmdGAyki
4 files changed,
+99 insertions,
-96 deletions
| @@ -6674,7 +6674,7 @@ | |||
| 6674 | 6674 | ||
| 6675 | 6675 | [[package]] | |
| 6676 | 6676 | name = "quasi-router" | |
| 6677 | - | version = "0.101.1" | |
| 6677 | + | version = "0.101.2" | |
| 6678 | 6678 | dependencies = [ | |
| 6679 | 6679 | "makeover-layout", | |
| 6680 | 6680 | ] | |
| @@ -10770,12 +10770,16 @@ | |||
| 10770 | 10770 | ] | |
| 10771 | 10771 | ||
| 10772 | 10772 | [[patch.unused]] | |
| 10773 | - | name = "synckit-client" | |
| 10774 | - | version = "0.10.0" | |
| 10773 | + | name = "kberg" | |
| 10774 | + | version = "0.1.0" | |
| 10775 | 10775 | ||
| 10776 | 10776 | [[patch.unused]] | |
| 10777 | - | name = "synckit-config" | |
| 10778 | - | version = "0.2.0" | |
| 10777 | + | name = "ops-status" | |
| 10778 | + | version = "0.1.0" | |
| 10779 | + | ||
| 10780 | + | [[patch.unused]] | |
| 10781 | + | name = "painhours" | |
| 10782 | + | version = "0.1.0" | |
| 10779 | 10783 | ||
| 10780 | 10784 | [[patch.unused]] | |
| 10781 | 10785 | name = "quasi-immediate" | |
| @@ -10794,13 +10798,9 @@ | |||
| 10794 | 10798 | version = "0.101.1" | |
| 10795 | 10799 | ||
| 10796 | 10800 | [[patch.unused]] | |
| 10797 | - | name = "kberg" | |
| 10798 | - | version = "0.1.0" | |
| 10801 | + | name = "synckit-client" | |
| 10802 | + | version = "0.10.0" | |
| 10799 | 10803 | ||
| 10800 | 10804 | [[patch.unused]] | |
| 10801 | - | name = "ops-status" | |
| 10802 | - | version = "0.1.0" | |
| 10803 | - | ||
| 10804 | - | [[patch.unused]] | |
| 10805 | - | name = "painhours" | |
| 10806 | - | version = "0.1.0" | |
| 10805 | + | name = "synckit-config" | |
| 10806 | + | version = "0.2.0" |
| @@ -39,39 +39,44 @@ | |||
| 39 | 39 | //! Askama on the element around it, where they were always a fact about that | |
| 40 | 40 | //! page's layout rather than about the control. | |
| 41 | 41 | ||
| 42 | + | use quasi_declare::declare; | |
| 43 | + | use quasi_router::Node; | |
| 42 | 44 | use quasi_router::screen::Act; | |
| 43 | - | use quasi_router::{Action, Node}; | |
| 44 | 45 | ||
| 45 | - | /// The route each export posts to, and the name the file is kept under. | |
| 46 | - | /// | |
| 47 | - | /// A read would be a link the browser downloads by itself; these are posts, | |
| 48 | - | /// because assembling the file is work the server does rather than a document | |
| 49 | - | /// it already has, and `Action::saves` is independent of the verb for exactly | |
| 50 | - | /// that reason. | |
| 51 | - | /// | |
| 52 | - | /// `awaiting` because the whole file is assembled before any of it comes back. | |
| 53 | - | /// Nothing countable to say about the wait: the row count is often known at the | |
| 54 | - | /// call site and the byte count never is, and `layout::Awaiting` takes a | |
| 55 | - | /// measurement rather than a stand-in for one. | |
| 56 | - | #[must_use] | |
| 57 | - | pub fn act(route: &str, filename: &str) -> Node { | |
| 58 | - | Node::Act(control("Export CSV", route, filename)) | |
| 46 | + | declare! { | |
| 47 | + | /// The route each export posts to, and the name the file is kept under. | |
| 48 | + | /// | |
| 49 | + | /// A read would be a link the browser downloads by itself; these are posts, | |
| 50 | + | /// because assembling the file is work the server does rather than a document | |
| 51 | + | /// it already has, and `Action::saves` is independent of the verb for exactly | |
| 52 | + | /// that reason. | |
| 53 | + | /// | |
| 54 | + | /// `awaiting` because the whole file is assembled before any of it comes back. | |
| 55 | + | /// Nothing countable to say about the wait: the row count is often known at the | |
| 56 | + | /// call site and the byte count never is, and `layout::Awaiting` takes a | |
| 57 | + | /// measurement rather than a stand-in for one. | |
| 58 | + | #[must_use] | |
| 59 | + | pub shape act(route: &str, filename: &str) -> Node; | |
| 60 | + | ||
| 61 | + | include control("Export CSV", route, filename); | |
| 59 | 62 | } | |
| 60 | 63 | ||
| 61 | - | /// The same sentence under a caller's own label. | |
| 62 | - | /// | |
| 63 | - | /// `Export CSV` is right for a control standing alone among others, which is | |
| 64 | - | /// every site [`act`] serves. It is wrong on the export portal, where six cards | |
| 65 | - | /// each name their own subject and the format is already in the card's meta | |
| 66 | - | /// line, so the control there reads `Download`. The label is the only thing | |
| 67 | - | /// that varies: what a caller must not restate is the sentence, which is here | |
| 68 | - | /// once and is what this module exists for. | |
| 69 | - | /// | |
| 70 | - | /// Returns the [`Act`] rather than a [`Node`] because the portal's controls sit | |
| 71 | - | /// in a [`quasi_router::Row`], which holds acts rather than nodes. | |
| 72 | - | #[must_use] | |
| 73 | - | pub fn control(label: &str, route: &str, filename: &str) -> Act { | |
| 74 | - | Act::new(label, Action::post(route).saving(filename).awaiting()) | |
| 64 | + | declare! { | |
| 65 | + | /// The same sentence under a caller's own label. | |
| 66 | + | /// | |
| 67 | + | /// `Export CSV` is right for a control standing alone among others, which is | |
| 68 | + | /// every site [`act`] serves. It is wrong on the export portal, where six cards | |
| 69 | + | /// each name their own subject and the format is already in the card's meta | |
| 70 | + | /// line, so the control there reads `Download`. The label is the only thing | |
| 71 | + | /// that varies: what a caller must not restate is the sentence, which is here | |
| 72 | + | /// once and is what this module exists for. | |
| 73 | + | /// | |
| 74 | + | /// Returns the [`Act`] rather than a [`Node`] because the portal's controls sit | |
| 75 | + | /// in a [`quasi_router::Row`], which holds acts rather than nodes. | |
| 76 | + | #[must_use] | |
| 77 | + | pub shape control(label: &str, route: &str, filename: &str) -> Act; | |
| 78 | + | ||
| 79 | + | act label to post route saving filename awaiting; | |
| 75 | 80 | } | |
| 76 | 81 | ||
| 77 | 82 | /// The same act as a fragment, for a template to drop in place. |
| @@ -31,7 +31,8 @@ | |||
| 31 | 31 | //! in -- "Following (12)" against "Follow (12)" -- so what is lost is a | |
| 32 | 32 | //! seven-tenths opacity on a button that says the same thing in words. | |
| 33 | 33 | ||
| 34 | - | use quasi_router::{Act, Action, Node, RegionKind, Slot}; | |
| 34 | + | use quasi_declare::declare; | |
| 35 | + | use quasi_router::Node; | |
| 35 | 36 | use quasi_webview::Webview; | |
| 36 | 37 | ||
| 37 | 38 | /// The region a press on this control replaces. | |
| @@ -46,31 +47,28 @@ | |||
| 46 | 47 | format!("follow-{target_type}-{target_id}") | |
| 47 | 48 | } | |
| 48 | 49 | ||
| 49 | - | /// The control, in whichever of its two states the viewer is in. | |
| 50 | - | /// | |
| 51 | - | /// The count rides in the label rather than beside it, which is what both | |
| 52 | - | /// templates did: a bare number next to a verb reads as a second control. | |
| 53 | - | #[must_use] | |
| 54 | - | pub fn control( | |
| 55 | - | target_type: &str, | |
| 56 | - | target_id: &str, | |
| 57 | - | is_following: bool, | |
| 58 | - | follower_count: i64, | |
| 59 | - | ) -> Node { | |
| 50 | + | declare! { | |
| 51 | + | /// The control, in whichever of its two states the viewer is in. | |
| 52 | + | /// | |
| 53 | + | /// The count rides in the label rather than beside it, which is what both | |
| 54 | + | /// templates did: a bare number next to a verb reads as a second control. | |
| 55 | + | #[must_use] | |
| 56 | + | pub shape control( | |
| 57 | + | target_type: &str, | |
| 58 | + | target_id: &str, | |
| 59 | + | is_following: bool, | |
| 60 | + | follower_count: i64 | |
| 61 | + | ) -> Node; | |
| 62 | + | ||
| 60 | 63 | let id = region(target_type, target_id); | |
| 61 | - | let route = format!("/api/follow/{target_type}/{target_id}"); | |
| 62 | - | let act = if is_following { | |
| 63 | - | Act::new( | |
| 64 | - | format!("Following ({follower_count})"), | |
| 65 | - | Action::delete(route).replacing(&id), | |
| 66 | - | ) | |
| 67 | - | } else { | |
| 68 | - | Act::new( | |
| 69 | - | format!("Follow ({follower_count})"), | |
| 70 | - | Action::post(route).replacing(&id), | |
| 71 | - | ) | |
| 72 | - | }; | |
| 73 | - | Node::Region(Slot::new(id, RegionKind::Group).with(Node::Act(act))) | |
| 64 | + | let route = "/api/follow/{target_type}/{target_id}"; | |
| 65 | + | ||
| 66 | + | region id as Group { | |
| 67 | + | given is_following { | |
| 68 | + | true -> act "Following ({follower_count})" to delete route replacing &id; | |
| 69 | + | false -> act "Follow ({follower_count})" to post route replacing &id; | |
| 70 | + | } | |
| 71 | + | } | |
| 74 | 72 | } | |
| 75 | 73 | ||
| 76 | 74 | /// The control as a fragment, for the route that answers a press. | |
| @@ -91,21 +89,22 @@ | |||
| 91 | 89 | )) | |
| 92 | 90 | } | |
| 93 | 91 | ||
| 94 | - | /// What a page shows when the viewer cannot follow: the count, or nothing. | |
| 95 | - | /// | |
| 96 | - | /// A signed-out reader, or a creator looking at their own profile. Both | |
| 97 | - | /// templates drew the number as plain text in that case and drew nothing at all | |
| 98 | - | /// when it was zero, which is the right reading: "0 followers" is a fact nobody | |
| 99 | - | /// wants published about them. | |
| 100 | - | #[must_use] | |
| 101 | - | pub fn count_only(follower_count: i64) -> Option<Node> { | |
| 102 | - | (follower_count > 0).then(|| { | |
| 103 | - | Node::text(if follower_count == 1 { | |
| 104 | - | "1 follower".to_owned() | |
| 105 | - | } else { | |
| 106 | - | format!("{follower_count} followers") | |
| 107 | - | }) | |
| 108 | - | }) | |
| 92 | + | declare! { | |
| 93 | + | /// What a page shows when the viewer cannot follow: the count, or nothing. | |
| 94 | + | /// | |
| 95 | + | /// A signed-out reader, or a creator looking at their own profile. Both | |
| 96 | + | /// templates drew the number as plain text in that case and drew nothing at all | |
| 97 | + | /// when it was zero, which is the right reading: "0 followers" is a fact nobody | |
| 98 | + | /// wants published about them. | |
| 99 | + | #[must_use] | |
| 100 | + | pub shape count_only(follower_count: i64) -> Option<Node>; | |
| 101 | + | ||
| 102 | + | let counted = given follower_count { | |
| 103 | + | 1 -> "1 follower", | |
| 104 | + | otherwise -> "{follower_count} followers", | |
| 105 | + | }; | |
| 106 | + | ||
| 107 | + | text counted when follower_count over 0; | |
| 109 | 108 | } | |
| 110 | 109 | ||
| 111 | 110 | #[cfg(test)] |
| @@ -38,22 +38,21 @@ | |||
| 38 | 38 | //! is a plain navigation in `static/item-upload.js` today and becomes an act | |
| 39 | 39 | //! whenever the files tab converts. | |
| 40 | 40 | ||
| 41 | - | use makeover_layout::Tone; | |
| 42 | - | use quasi_router::Action; | |
| 41 | + | use quasi_declare::declare; | |
| 43 | 42 | use quasi_router::screen::Act; | |
| 44 | 43 | ||
| 45 | - | /// The delete button for one version's row. | |
| 46 | - | /// | |
| 47 | - | /// Both ids are needed for the address, which is item and version together. | |
| 48 | - | /// The row it removes is the one it sits in, so nothing has to name it. | |
| 49 | - | #[must_use] | |
| 50 | - | pub fn act(item_id: &str, version_id: &str) -> Act { | |
| 51 | - | Act::new( | |
| 52 | - | "Delete", | |
| 53 | - | Action::delete(format!("/api/items/{item_id}/versions/{version_id}")).replacing_enclosing(), | |
| 54 | - | ) | |
| 55 | - | .tone(Tone::Danger) | |
| 56 | - | .confirm("Delete this version?") | |
| 44 | + | declare! { | |
| 45 | + | /// The delete button for one version's row. | |
| 46 | + | /// | |
| 47 | + | /// Both ids are needed for the address, which is item and version together. | |
| 48 | + | /// The row it removes is the one it sits in, so nothing has to name it. | |
| 49 | + | #[must_use] | |
| 50 | + | pub shape act(item_id: &str, version_id: &str) -> Act; | |
| 51 | + | ||
| 52 | + | act "Delete" to delete "/api/items/{item_id}/versions/{version_id}" replacing_enclosing { | |
| 53 | + | tone Danger; | |
| 54 | + | confirm "Delete this version?"; | |
| 55 | + | } | |
| 57 | 56 | } | |
| 58 | 57 | ||
| 59 | 58 | #[cfg(test)] |