max / quasi
| 1 | //! The seam between a description and bytes. |
| 2 | //! |
| 3 | //! No host adapter emits markup, and that is the important decision in the set |
| 4 | //! of them. |
| 5 | //! |
| 6 | //! A hosted axum route and a Tauri custom-protocol handler both serve HTML to a |
| 7 | //! webview. If the HTML were generated in either, the other would need a second |
| 8 | //! copy of it, and two copies of a markup emitter is the divergence the whole |
| 9 | //! stack exists to end. So markup belongs to the webview renderer, which is |
| 10 | //! `makeover-webview`, and every adapter takes it as a parameter. |
| 11 | //! |
| 12 | //! Today `makeover-webview` emits a stylesheet, form fields and table cells, |
| 13 | //! which is phase A. Whole-screen markup is phase B and is not written, so an |
| 14 | //! app supplies its own [`Serves`] in the meantime. When phase B lands, the |
| 15 | //! implementation moves there and every host adapter picks it up at once |
| 16 | //! instead of one at a time. |
| 17 | |
| 18 | use ; |
| 19 | |
| 20 | /// Turns a description into the bytes a browser gets. |
| 21 | /// |
| 22 | /// An HTTP host's contract, and not the thing the three renderers share. A |
| 23 | /// terminal answers cells in a buffer rather than a `String`, and a content |
| 24 | /// type is an HTTP header. What a webview, a terminal and an egui frame have |
| 25 | /// in common is the description itself, which is a type they already take; a |
| 26 | /// trait over that would have one method taking `&Screen` and returning ``, |
| 27 | /// which buys nothing. Two renderers sharing no trait is a fact about |
| 28 | /// renderers rather than a hole. |
| 29 | /// |
| 30 | /// Two methods, because [`Response`](quasi_router::Response) has two members |
| 31 | /// and a renderer genuinely does different work for each: a screen is a |
| 32 | /// document and a fragment is the inside of one element. |
| 33 | /// |
| 34 | /// Errors are not a third method. An adapter turns a |
| 35 | /// [`RouteError`](quasi_router::RouteError) into a [`Node::Notice`] and asks |
| 36 | /// for a fragment, so failure becomes UI through the same path as everything |
| 37 | /// else. That is decision 9 holding at the boundary rather than only in the |
| 38 | /// router. |
| 39 | |
| 40 | /// A whole document. |
| 41 | ; |
| 42 | |
| 43 | /// The inside of one region. |
| 44 | ; |
| 45 | |
| 46 | /// One region's new contents, carried beside the region being replaced. |
| 47 | /// |
| 48 | /// [`Response::invalidates`](quasi_router::Response::invalidates): the row |
| 49 | /// the write was aimed at is the [`fragment`](Self::fragment), and the |
| 50 | /// count in the header is one of these. Both travel on one answer, so the |
| 51 | /// two never disagree and no second request is made for a fact the router |
| 52 | /// already had. |
| 53 | /// |
| 54 | /// Unlike the other two this carries the region's id, because an |
| 55 | /// out-of-band update is applied by name rather than by where it was |
| 56 | /// aimed. A fragment does not need one: the router said where it goes in |
| 57 | /// [`RETARGET`](crate::htmx::RETARGET). |
| 58 | /// |
| 59 | /// # Why it defaults to nothing |
| 60 | /// |
| 61 | /// A renderer answering something other than markup has no out-of-band |
| 62 | /// channel to put this in, the same reason [`content_type`](Self::content_type) |
| 63 | /// is overridable. Returning nothing is that renderer saying so, and it is |
| 64 | /// the honest default: a JSON client is told what changed by the payload it |
| 65 | /// already parses. |
| 66 | |
| 67 | let _ = ; |
| 68 | Stringnew |
| 69 | |
| 70 | |
| 71 | /// The inside of the overlay container, for a screen drawn over the page. |
| 72 | /// |
| 73 | /// [`Outcome::Over`](quasi_router::Outcome::Over). No document and no |
| 74 | /// shell: what is under it already has both, and replacing them is exactly |
| 75 | /// what an overlay is not. |
| 76 | /// |
| 77 | /// # Why it defaults to the whole screen |
| 78 | /// |
| 79 | /// A renderer with no overlay container cannot layer anything, and the |
| 80 | /// honest fallback is to draw the screen: the user reaches the palette, |
| 81 | /// without it floating. Returning nothing would be the silent no-op |
| 82 | /// `Outcome`'s exhaustiveness exists to prevent, and this is the same |
| 83 | /// argument one level in. |
| 84 | |
| 85 | self.screen |
| 86 | |
| 87 | |
| 88 | /// One field's suggestion list, for the field that owns it. |
| 89 | /// |
| 90 | /// [`Outcome::Suggestions`](quasi_router::Outcome::Suggestions). The field |
| 91 | /// is named by [`Field::name`](quasi_router::Field::name) and never by an |
| 92 | /// id: the list belongs to the control, so a renderer that needs a document |
| 93 | /// id derives one from the name and says so in |
| 94 | /// [`suggestions_target`](Self::suggestions_target). |
| 95 | /// |
| 96 | /// # Why this has no default |
| 97 | /// |
| 98 | /// A default returning nothing is the silent no-op |
| 99 | /// [`Outcome`](quasi_router::Outcome)'s exhaustiveness exists to prevent, |
| 100 | /// one level in: the route answered with candidates, the user typed to get |
| 101 | /// them, and a renderer that quietly drew none would look like a route with |
| 102 | /// nothing to suggest. [`overlay`](Self::overlay) can default because |
| 103 | /// drawing the screen unlayered is a worse *rendering* of the same answer; |
| 104 | /// there is no such fallback here. |
| 105 | ; |
| 106 | |
| 107 | /// The id of the element a field's suggestion list is drawn into. |
| 108 | /// |
| 109 | /// [`overlay_target`](Self::overlay_target)'s counterpart, and `None` means |
| 110 | /// the same thing: this renderer has no container to aim at, so the answer |
| 111 | /// is sent without a retarget and the client puts it wherever it was aimed. |
| 112 | /// |
| 113 | /// Derived from the field's name rather than authored, which is what makes |
| 114 | /// a suggestion list a thing a field owns rather than two elements a |
| 115 | /// description has to keep pointing at each other. |
| 116 | |
| 117 | let _ = field; |
| 118 | None |
| 119 | |
| 120 | |
| 121 | /// The inside of a popover container, for a screen drawn at a point. |
| 122 | /// |
| 123 | /// [`Outcome::Anchored`](quasi_router::Outcome::Anchored). No document and |
| 124 | /// no shell, for [`overlay`](Self::overlay)'s reason: what is under it has |
| 125 | /// both. |
| 126 | /// |
| 127 | /// The anchor is not a parameter because it is already spent. It named the |
| 128 | /// container this lands in, which is what |
| 129 | /// [`anchored_target`](Self::anchored_target) answered, and asking a |
| 130 | /// renderer to write the same fact into the markup as well would be one |
| 131 | /// thing decided twice. |
| 132 | /// |
| 133 | /// # Why it defaults to the overlay |
| 134 | /// |
| 135 | /// A renderer with no popover container cannot draw at a point, and an |
| 136 | /// unanchored overlay is the honest fallback the same way an unlayered |
| 137 | /// screen is [`overlay`](Self::overlay)'s: the user reaches the menu, |
| 138 | /// without it floating where they pressed. Drawing nothing would be the |
| 139 | /// silent no-op [`Outcome`](quasi_router::Outcome)'s exhaustiveness exists |
| 140 | /// to prevent. |
| 141 | |
| 142 | self.overlay |
| 143 | |
| 144 | |
| 145 | /// The id of the element an anchored screen is drawn into. |
| 146 | /// |
| 147 | /// [`overlay_target`](Self::overlay_target)'s counterpart, taking the |
| 148 | /// anchor because that is the whole difference: an overlay lands in the one |
| 149 | /// container an app has, and this lands in the container belonging to the |
| 150 | /// thing that was anchored to. |
| 151 | /// |
| 152 | /// `None` means what it means on the other two: this renderer has no such |
| 153 | /// container, so the answer is sent without a retarget and lands where it |
| 154 | /// was aimed. That is also the honest answer for an anchor naming something |
| 155 | /// this document does not have — a region that is not on the screen, a |
| 156 | /// control carrying no [`Act::id`](quasi_router::Act::id) — which is a |
| 157 | /// description bug and is treated the way every other one is. |
| 158 | |
| 159 | let _ = anchor; |
| 160 | None |
| 161 | |
| 162 | |
| 163 | /// The id of the element an overlay is drawn into. |
| 164 | /// |
| 165 | /// `None` says this renderer has no overlay container, which is what makes |
| 166 | /// [`overlay`](Self::overlay)'s default a whole-screen swap rather than |
| 167 | /// markup aimed at an element that is not there. |
| 168 | |
| 169 | None |
| 170 | |
| 171 | |
| 172 | /// What the document says it is. |
| 173 | /// |
| 174 | /// Overridable for a renderer answering something other than HTML, which is |
| 175 | /// the seam a JSON client or a test harness needs. Defaults to HTML because |
| 176 | /// that is what every webview host wants. |
| 177 | |
| 178 | "text/html; charset=utf-8" |
| 179 | |
| 180 | |
| 181 |