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. It was |
| 23 | /// called `Render` until 2026-08-12, which cost the first hour of writing the |
| 24 | /// second renderer: `quasi-tui` implements none of this and cannot. A terminal |
| 25 | /// answers cells in a buffer rather than a `String`, and a content type is an |
| 26 | /// HTTP header. What a webview, a terminal and an egui frame have in common is |
| 27 | /// the description itself, which is a type they already take; a trait over that |
| 28 | /// would have one method taking `&Screen` and returning `()`, which buys |
| 29 | /// nothing. Two renderers sharing no trait is a fact about renderers rather |
| 30 | /// than a hole. |
| 31 | /// |
| 32 | /// Two methods, because [`Response`](quasi_router::Response) has two members |
| 33 | /// and a renderer genuinely does different work for each: a screen is a |
| 34 | /// document and a fragment is the inside of one element. |
| 35 | /// |
| 36 | /// Errors are not a third method. An adapter turns a |
| 37 | /// [`RouteError`](quasi_router::RouteError) into a [`Node::Notice`] and asks |
| 38 | /// for a fragment, so failure becomes UI through the same path as everything |
| 39 | /// else. That is decision 9 holding at the boundary rather than only in the |
| 40 | /// router. |
| 41 | |
| 42 | /// A whole document. |
| 43 | ; |
| 44 | |
| 45 | /// The inside of one region. |
| 46 | ; |
| 47 | |
| 48 | /// One region's new contents, carried beside the region being replaced. |
| 49 | /// |
| 50 | /// [`Response::invalidates`](quasi_router::Response::invalidates): the row |
| 51 | /// the write was aimed at is the [`fragment`](Self::fragment), and the |
| 52 | /// count in the header is one of these. Both travel on one answer, so the |
| 53 | /// two never disagree and no second request is made for a fact the router |
| 54 | /// already had. |
| 55 | /// |
| 56 | /// Unlike the other two this carries the region's id, because an |
| 57 | /// out-of-band update is applied by name rather than by where it was |
| 58 | /// aimed. A fragment does not need one: the router said where it goes in |
| 59 | /// [`RETARGET`](crate::htmx::RETARGET). |
| 60 | /// |
| 61 | /// # Why it defaults to nothing |
| 62 | /// |
| 63 | /// A renderer answering something other than markup has no out-of-band |
| 64 | /// channel to put this in, the same reason [`content_type`](Self::content_type) |
| 65 | /// is overridable. Returning nothing is that renderer saying so, and it is |
| 66 | /// the honest default: a JSON client is told what changed by the payload it |
| 67 | /// already parses. |
| 68 | |
| 69 | let _ = ; |
| 70 | Stringnew |
| 71 | |
| 72 | |
| 73 | /// The inside of the overlay container, for a screen drawn over the page. |
| 74 | /// |
| 75 | /// [`Outcome::Over`](quasi_router::Outcome::Over). No document and no |
| 76 | /// shell: what is under it already has both, and replacing them is exactly |
| 77 | /// what an overlay is not. |
| 78 | /// |
| 79 | /// # Why it defaults to the whole screen |
| 80 | /// |
| 81 | /// A renderer with no overlay container cannot layer anything, and the |
| 82 | /// honest fallback is to draw the screen: the user reaches the palette, |
| 83 | /// without it floating. Returning nothing would be the silent no-op |
| 84 | /// `Outcome`'s exhaustiveness exists to prevent, and this is the same |
| 85 | /// argument one level in. |
| 86 | |
| 87 | self.screen |
| 88 | |
| 89 | |
| 90 | /// The id of the element an overlay is drawn into. |
| 91 | /// |
| 92 | /// `None` says this renderer has no overlay container, which is what makes |
| 93 | /// [`overlay`](Self::overlay)'s default a whole-screen swap rather than |
| 94 | /// markup aimed at an element that is not there. |
| 95 | |
| 96 | None |
| 97 | |
| 98 | |
| 99 | /// What the document says it is. |
| 100 | /// |
| 101 | /// Overridable for a renderer answering something other than HTML, which is |
| 102 | /// the seam a JSON client or a test harness needs. Defaults to HTML because |
| 103 | /// that is what every webview host wants. |
| 104 | |
| 105 | "text/html; charset=utf-8" |
| 106 | |
| 107 | |
| 108 |