//! What a renderer is, for the one question where the three differ. //! //! //! //! The renderers share no trait and looking for one is the wrong move, which //! this crate's header already says. This is not that: it is a single fact //! about a renderer that a *description* has no way to ask and that the //! [`Destination::Local`](crate::Destination::Local) mark needs an answer to. /// Whether a renderer has to be told what is local. /// /// [`Destination::Local`](crate::Destination::Local). /// /// ```text /// Client -> quasi-immediate (egui), quasi-tui may ignore the mark /// Hybrid -> quasi-webview must read it /// ``` /// /// # Why classify at all, rather than let each renderer ignore what it likes /// /// A client app has no real need to model the two differently, especially an /// immediate-mode one. Every frame redraws from memory, so arrow keys moving a /// highlight is not a special kind of thing there — it is what a list does. The /// split exists because a browser has no built-in stateful control, so someone /// has to say whether a keystroke goes to the server. /// /// Two of three renderers can therefore skip the whole area, and the choice was /// between saying why and leaving it to convention. Convention erodes: the next /// person to read `quasi-tui` and find no handling for a mark the vocabulary /// carries reads it as an omission and fixes it, and the fix is a distinction /// that means nothing in a terminal. Naming the class states the reason in the /// one place both renderers can point at. /// /// Rejected: every renderer models the split — uniform, and egui and the TUI /// carry a distinction that means nothing to them. Rejected: local interaction /// is browser-only with the others degrading — cheapest, and audiofiles is a /// live egui consumer, so the loss would be real rather than hypothetical. /// /// # What this is not /// /// Not a capability list and not a feature flag. It answers one question and /// gaining a second variant is a sign something else is being smuggled in. /// Whether a terminal can open a browser, whether a host can upload — those are /// [`Destination::External`](crate::Destination::External) and /// [`Action::by_host`](crate::Action::by_host), which say their piece on the /// action rather than about the renderer. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Renderer { /// Everything is local by construction: the renderer holds the state it /// draws and redraws it from memory. /// /// A local action is something such a renderer already performs natively — /// moving a highlight, closing a notice — so the mark tells it nothing it /// did not know. It is free to ignore it, and the vocabulary promises it /// loses nothing by doing so. /// /// What it may not do is dispatch one. There is no route on a local /// destination, so [`Destination::route`](crate::Destination::route) /// answers `None` and the renderer's own dispatch already declines; that is /// the floor, and it holds without any code being written against this /// type. Client, /// The split is real and must be honoured. /// /// A hybrid renderer emits markup that some other runtime performs later, /// so it has to decide at emit time whether a keystroke becomes a request. /// Getting that wrong is not a slower screen, it is a route asked on every /// arrow key. Hybrid, } impl Renderer { /// Whether this renderer has to read the locality mark. #[must_use] pub const fn reads_locality(self) -> bool { matches!(self, Self::Hybrid) } }