Skip to main content

max / quasi

State that a description never names client-only state
Author: Max Johnson <me@maxj.phd> · 2026-08-28 20:01 UTC
Signed with PGP, not checked
Commit: b9e472760ce09bde09355f6b4a49f5adfd453326
Parent: e3524cd
1 file changed, +55 insertions, -0 deletions
@@ -3592,6 +3592,51 @@
3592 3592 /// per-child name, and they are here rather than in `makeover-layout` for
3593 3593 /// the reason [`Node::Select`]'s `chosen` is: a layer that defers every
3594 3594 /// address does not hold what is picked either.
3595 + ///
3596 + /// # A description never names client-only state
3597 + ///
3598 + /// Decided 2026-08-28, `ad9c5615`, extending what this field already set.
3599 + /// A description says what a region *is*: these panels are peers, show one.
3600 + /// State that lives only in the client and never reaches a handler is not
3601 + /// part of that. Each renderer decides it, exactly as it already decides
3602 + /// scroll position, focus and the duration of an undo window.
3603 + ///
3604 + /// The consequence is worth stating plainly rather than discovering later:
3605 + /// **a screen has no way to say a preference should persist.** That is a
3606 + /// deliberate limit. A description that wanted to say "remember which tab
3607 + /// the reader was on across visits" would be naming storage, and storage is
3608 + /// the host's.
3609 + ///
3610 + /// Three behaviours settle as renderer policy under this rule, recorded
3611 + /// here because a reader meets the question at this field:
3612 + ///
3613 + /// - **Section tabs switched without a round trip.** This field already
3614 + /// answers it: the description says the panels are peers and one is up,
3615 + /// and the renderer toggles and rewrites the URL. MNW defines
3616 + /// `switchSectionTab` verbatim in three bundles (`static/page-item-2.js`,
3617 + /// `static/page-library-downloads.js`, `static/page-project.js`) and
3618 + /// reaches a fourth site through the delegated handler in
3619 + /// `static/actions-pages.js`. Three definitions, four call sites, and none
3620 + /// of them is something a description should have been carrying.
3621 + /// - **A persisted view preference.** The renderer decides whether it
3622 + /// persists and where; nothing in the description mentions storage. The
3623 + /// persist-across-swap behaviour has **one** implementation in MNW,
3624 + /// `static/page-discover.js:176-178`, not the two an earlier count
3625 + /// claimed. The count is recorded because it is what made a vocabulary
3626 + /// addition look worth buying, and at one site it is not.
3627 + /// - **Loading a region's contents on first reveal.** [`Readiness`] says
3628 + /// what a region shows while it waits; what makes it *start* is the
3629 + /// renderer's. MNW does it twice, license text on a `<details>` toggle
3630 + /// and a video `src` on first `play`, each guarding with a `loaded` flag.
3631 + ///
3632 + /// One fragility worth knowing before a conversion, since a converter will
3633 + /// arrive here first: MNW's `static/page-project-2.js` binds its
3634 + /// `.view-btn` handlers directly at script load rather than by delegation.
3635 + /// The container it binds into is never an htmx target today, so that is
3636 + /// correct as written. If it ever becomes one, the handlers and the view
3637 + /// state both break silently.
3638 + ///
3639 + /// [`Readiness`]: layout::Readiness
3595 3640 pub showing: layout::Showing,
3596 3641 /// Which child is up, when only one of them is.
3597 3642 ///
@@ -3949,6 +3994,11 @@
3949 3994 /// a host draws a strip of names or a prev/next row falls out of whether
3950 3995 /// the children carry a [`label`](Self::label), never out of the widget's
3951 3996 /// name.
3997 + ///
3998 + /// `shown` is where the region starts, not where it stays. Whether a later
3999 + /// visit comes back to the same child is the renderer's, and there is no
4000 + /// way to say otherwise here. See [`showing`](Self::showing) for the rule
4001 + /// and what it settles.
3952 4002 #[must_use]
3953 4003 pub fn showing_one(mut self, shown: usize) -> Self {
3954 4004 self.showing = layout::Showing::One;
@@ -3961,6 +4011,11 @@
3961 4011 /// Disclosure. `None` is the closed state and is a legal resting place,
3962 4012 /// which is the whole of what separates this from
3963 4013 /// [`showing_one`](Self::showing_one).
4014 + ///
4015 + /// A closed disclosure that fetches its contents when it first opens is
4016 + /// describable as `None` here plus a [`Readiness`](layout::Readiness) for
4017 + /// the wait. What makes the fetch start is the renderer's, under the rule
4018 + /// on [`showing`](Self::showing).
3964 4019 #[must_use]
3965 4020 pub fn showing_at_most_one(mut self, shown: Option<usize>) -> Self {
3966 4021 self.showing = layout::Showing::AtMostOne;