Skip to main content

max / makenotwork

Put both forum panes on the residual seam, and refuse a staged dispatch Nine of seventeen. The forum memberships module is two screens sharing one table, so it gets two residuals, and they are the first panel screens on the seam: a panel answers a fragment plus the `HX-Retarget` naming the region it changed, with no document, no shell and no `Screen`. `served_panel_mount` is that, and the nest under it is still the adapter's -- a panel's writes answer a `Response` the router builds per request and there is nothing to derive about a deletion. The seam's read merges in front, which works because the adapter mounts as a fallback on purpose. **A staged dispatch was silently serving the wrong page.** The derivation finds a branch by rendering the screen with one guard changed and reading the difference. A dispatch is not that: its arms replace each other at one position, so there is no insertion to measure. The staged twin read `plan.arm(id)`, which answers zero, so exactly one arm rendered and the residual held it as a literal with the others gone -- the forum settings pane compiled to "You haven't joined any forum communities yet." and would have served that to every reader with a membership. quasi-declare refuses it now, naming the rewrite: two guards render the same and derive a branch each. Every dispatch in the tree is two-way over a bool, so that is a rewrite and not a loss. `Op::Arms` with a derivation that renders once per arm is the general answer and is a feature rather than a fix. The upstream sentence is written where it is staged rather than passed to `own_prose`, for the reason /git/{owner}'s clone URL was: the shape's parameter is the whole markdown source, so the address has to be a hole inside it. The derivation then parses the link with a sentinel in its destination and the residual holds `<a href="` around a gap.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session
https://claude.ai/code/session_01P8ostB2UmZJGj5WjSHRSot
Author: Max Johnson <me@maxj.phd> · 2026-09-08 02:09 UTC
Commit: 4621d89a1c4cf6e1af5a3b5f42b10867e176842e
Parent: f16d222
4 files changed, +282 insertions, -25 deletions
@@ -65,7 +65,7 @@
65 65 const UPSTREAM_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
66 66
67 67 /// One community this reader belongs to, as the screens need it.
68 - pub struct MembershipView {
68 + pub(crate) struct MembershipView {
69 69 community: String,
70 70 profile_url: String,
71 71 role: String,
@@ -105,6 +105,30 @@
105 105 ))
106 106 }
107 107
108 + /// The one read both panes make, for the mounts that serve them from residuals.
109 + ///
110 + /// Answers the memberships and where they are, which is what both panes fill
111 + /// from. A pane that is not configured has no base and no memberships, and
112 + /// `library_screen` already treats those as one state.
113 + pub(crate) fn reading(viewer: &Viewer) -> Result<(Vec<MembershipView>, String), RouteError> {
114 + let Some(base) = configured_base(viewer) else {
115 + return Ok((Vec::new(), String::new()));
116 + };
117 + let memberships = fetch(viewer, viewer.reader()?, &base);
118 + Ok((memberships, base))
119 + }
120 +
121 + /// The same read for the settings pane, which refuses when nothing is
122 + /// configured rather than drawing an empty list. See [`settings_screen`].
123 + pub(crate) fn settings_reading(
124 + viewer: &Viewer,
125 + ) -> Result<(Vec<MembershipView>, String), RouteError> {
126 + let base = configured_base(viewer)
127 + .ok_or_else(|| RouteError::not_found("forums are not configured"))?;
128 + let memberships = fetch(viewer, viewer.reader()?, &base);
129 + Ok((memberships, base))
130 + }
131 +
108 132 /// Where Multithreaded lives, if it lives anywhere.
109 133 fn configured_base(viewer: &Viewer) -> Option<String> {
110 134 viewer.app.config.integrations.mt_base_url.clone()
@@ -173,16 +197,25 @@
173 197 /// one word in it that goes somewhere, and splitting it into text, an act
174 198 /// and more text is how a sentence stops reading as a sentence in every
175 199 /// host.
200 + ///
201 + /// [`super::own_prose`]'s body, written here rather than called, because the
202 + /// address has to be a hole **inside** the markdown. Handed to `own_prose`
203 + /// the sentence is formatted first, so a staged shape gets one sentinel
204 + /// where the whole document should be; written here the derivation parses
205 + /// the link with a sentinel in its destination and the residual holds
206 + /// `<a href="` around a gap. `own_prose` carries the rule.
207 + #[staged]
176 208 shape upstream_line(base: &str) -> Node;
177 209
178 - include super::own_prose(
179 - "Your memberships across [Multithreaded]({base}) forum communities."
180 - );
210 + rich "Your memberships across [Multithreaded]({base}) forum communities." {
211 + trust quasi_router::Trust::Trusted;
212 + }
181 213 }
182 214
183 215 declare! {
184 216 /// Everything inside the library's tab pane.
185 - shape library_pane(memberships: &[MembershipView], base: &str) -> Node;
217 + #[staged]
218 + pub(crate) shape library_pane(memberships: &[MembershipView], base: &str) -> Node;
186 219
187 220 region LIBRARY_REGION as Pane {
188 221 // The way out is offered only when there is somewhere to send them,
@@ -204,16 +237,19 @@
204 237 ///
205 238 /// The heading and the line are drawn either way here, unlike the library's,
206 239 /// which is the one real difference between the two screens.
207 - shape settings_pane(memberships: &[MembershipView], base: &str) -> Node;
240 + #[staged]
241 + pub(crate) shape settings_pane(memberships: &[MembershipView], base: &str) -> Node;
208 242
209 243 region SETTINGS_REGION as Pane {
210 244 section "Forum Communities";
211 245 include upstream_line(base);
212 246
213 - given memberships.is_empty() {
214 - true -> empty "You haven't joined any forum communities yet.";
215 - otherwise -> include table(memberships);
216 - }
247 + // Two guards rather than a dispatch, which renders the same and is
248 + // what the seam can derive: a dispatch's arms replace each other at one
249 + // position, so only one reaches the residual and the rest are lost. See
250 + // quasi-declare's `Emission::Given`, which refuses it.
251 + empty "You haven't joined any forum communities yet." when memberships.is_empty();
252 + include table(memberships) unless memberships.is_empty();
217 253 }
218 254 }
219 255
@@ -224,6 +260,7 @@
224 260 /// columns out of it, so every membership contributes the same four cells.
225 261 /// Nothing is paged, so no `more`: this is a whole set the handler already
226 262 /// counted.
263 + #[staged]
227 264 shape table(memberships: &[MembershipView]) -> Node;
228 265
229 266 table {
@@ -266,6 +303,21 @@
266 303 Webview::new().with_shell(viewer.shell())
267 304 }
268 305
306 + /// One membership as the tests draw it.
307 + ///
308 + /// Module-level rather than inside `mod tests` because `quasi::residuals` needs
309 + /// one too, and `MembershipView` is this module's own type. Test-only.
310 + #[cfg(test)]
311 + pub(crate) fn sample(community: &str, role: &str) -> MembershipView {
312 + MembershipView {
313 + community: community.into(),
314 + profile_url: format!("https://mt.example.com/p/{community}/u/max"),
315 + role: role.into(),
316 + posts: "12".into(),
317 + joined: "Aug 10, 2026".into(),
318 + }
319 + }
320 +
269 321 #[cfg(test)]
270 322 mod tests {
271 323 use quasi_axum::Serves;
@@ -273,15 +325,7 @@
273 325
274 326 use super::*;
275 327
276 - fn membership(community: &str, role: &str) -> MembershipView {
277 - MembershipView {
278 - community: community.into(),
279 - profile_url: format!("https://mt.example.com/p/{community}/u/max"),
280 - role: role.into(),
281 - posts: "12".into(),
282 - joined: "Aug 10, 2026".into(),
283 - }
284 - }
328 + use super::sample as membership;
285 329
286 330 fn render(node: &Node) -> String {
287 331 Webview::new().fragment(node)
@@ -531,11 +531,19 @@
531 531 // the same table under different chrome.
532 532 (
533 533 forum_memberships::LIBRARY_PATH,
534 - mount(
534 + served_panel_mount(
535 535 app,
536 - forum_memberships::library_screen,
536 + forum_memberships::LIBRARY_REGION,
537 537 &[],
538 538 forum_memberships::renderer,
539 + |viewer, _| {
540 + let (memberships, base) = forum_memberships::reading(viewer)?;
541 + Ok(forum_memberships::library_pane_serve(
542 + &residuals::FORUMS_LIBRARY,
543 + &memberships,
544 + &base,
545 + ))
546 + },
539 547 ),
540 548 ),
541 549 (
@@ -566,11 +574,19 @@
566 574 ),
567 575 (
568 576 forum_memberships::SETTINGS_PATH,
569 - mount(
577 + served_panel_mount(
570 578 app,
571 - forum_memberships::settings_screen,
579 + forum_memberships::SETTINGS_REGION,
572 580 &[],
573 581 forum_memberships::renderer,
582 + |viewer, _| {
583 + let (memberships, base) = forum_memberships::settings_reading(viewer)?;
584 + Ok(forum_memberships::settings_pane_serve(
585 + &residuals::FORUMS_SETTINGS,
586 + &memberships,
587 + &base,
588 + ))
589 + },
574 590 ),
575 591 ),
576 592 ]
@@ -798,6 +814,74 @@
798 814 ]
799 815 }
800 816
817 + /// A panel whose region is already written, mounted.
818 + ///
819 + /// The seam's mount for a screen in [`PATHS`], which is a different answer from
820 + /// a document's: a panel is fetched by a page that already exists, so what it
821 + /// returns is a fragment plus the `HX-Retarget` naming the region it changed.
822 + /// There is no document, no shell and no `Screen` -- which is why this is not
823 + /// [`served_document_mount`] with a flag.
824 + ///
825 + /// # The nest is still the adapter's, and the read is merged in front
826 + ///
827 + /// A panel screen's writes stay on the adapter: they answer a `Response` the
828 + /// router builds per request and there is nothing to derive about a deletion.
829 + /// So the nest is [`nest`] with no read registered, and the seam's `GET /` goes
830 + /// on the router in front of it. The adapter mounts as a fallback, deliberately
831 + /// (see `quasi_axum::Adapter::into_router`), so a route added here wins without
832 + /// anything being taken away.
833 + ///
834 + /// # The headers are the ones `quasi_http` would have set
835 + ///
836 + /// `Content-Type: text/html; charset=utf-8` and `HX-Retarget: #region`, which is
837 + /// exactly what `respond` emits for `Outcome::Fragment`. Written out rather than
838 + /// reached through, because reaching through means building the `Response` this
839 + /// path exists to avoid building.
840 + fn served_panel_mount(
841 + app: &AppState,
842 + region: &'static str,
843 + writes: &[(quasi_router::Method, &'static str, Screen)],
844 + renderer: fn(&Viewer) -> quasi_webview::Webview,
845 + answer: fn(&Viewer, &Carried) -> Result<String, quasi_router::RouteError>,
846 + ) -> axum::Router {
847 + let held = app.clone();
848 + nest(app, None, writes, renderer).route(
849 + "/",
850 + axum::routing::get(move |mut parts: axum::http::request::Parts| {
851 + let app = held.clone();
852 + async move {
853 + use axum::response::IntoResponse as _;
854 +
855 + // The gate is `Audience::Reader`, the same as the nest beside
856 + // it, so a signed-out request is refused before the handler
857 + // runs. A panel is fetched by a page that already checked, so
858 + // the adapter's bare refusal is the right answer here and the
859 + // branded 401 is the document mounts'.
860 + let viewer = match viewer_factory(app, Audience::Reader)(&parts).await {
861 + Ok(viewer) => viewer,
862 + Err(refusal) => {
863 + return axum::http::StatusCode::from_u16(refusal.class.http_status())
864 + .unwrap_or(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
865 + .into_response();
866 + }
867 + };
868 +
869 + let carried = Carried::of(&mut parts).await;
870 + match answer(&viewer, &carried) {
871 + Ok(markup) => (
872 + [(quasi_axum::htmx::RETARGET, format!("#{region}"))],
873 + axum::response::Html(markup),
874 + )
875 + .into_response(),
876 + Err(refusal) => axum::http::StatusCode::from_u16(refusal.class.http_status())
877 + .unwrap_or(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
878 + .into_response(),
879 + }
880 + }
881 + }),
882 + )
883 + }
884 +
801 885 /// A described document a reader NAVIGATES to with or without a session.
802 886 ///
803 887 /// [`document_mount`] with the two things that make it gated removed: the
@@ -1321,6 +1405,11 @@
1321 1405 // mounted unconditionally, so the thing to count is the adapter call
1322 1406 // each entry makes. Not `mount(app,`: rustfmt breaks the longer entries
1323 1407 // across lines and that token then finds three of the six.
1408 + //
1409 + // `served_panel_mount(` ends in the same token and is counted with
1410 + // them, which is what this wants: a panel that moves onto the residual
1411 + // seam is still a screen `PATHS` has to list. `writes_only(` does not,
1412 + // which is also right -- those nests answer no address of their own.
1324 1413 let registered = mounted.matches("mount(").count();
1325 1414 assert_eq!(
1326 1415 registered,
@@ -81,6 +81,14 @@
81 81 ("GIT_REPOS", |plan| {
82 82 Node::Region(super::git_repos::page_region_staged(plan))
83 83 }),
84 + // One module, two screens: the library tab and the settings section are
85 + // the same table under different chrome, so each gets its own residual.
86 + ("FORUMS_LIBRARY", |plan| {
87 + super::forum_memberships::library_pane_staged(plan)
88 + }),
89 + ("FORUMS_SETTINGS", |plan| {
90 + super::forum_memberships::settings_pane_staged(plan)
91 + }),
84 92 ]
85 93 }
86 94
@@ -569,6 +577,48 @@
569 577 );
570 578 }
571 579
580 + /// Both forum panes fill to what the renderer builds.
581 + ///
582 + /// The first panel screens on the seam, and the first module with two of
583 + /// them: the library tab and the settings section draw the same table under
584 + /// different chrome, so each has a residual and both are checked here.
585 + ///
586 + /// Empty and populated, and with the upstream configured and not. The base
587 + /// address is a hole inside the sentence's markdown -- the link's own
588 + /// destination -- so an empty one is the shape that would show a residual
589 + /// with the address baked into a literal.
590 + #[test]
591 + fn both_forum_residuals_fill_to_what_the_renderer_builds() {
592 + use crate::quasi::forum_memberships::{
593 + library_pane, library_pane_serve, sample, settings_pane, settings_pane_serve,
594 + };
595 + use quasi_axum::Serves as _;
596 +
597 + let none: Vec<_> = Vec::new();
598 + let some = vec![
599 + sample("rust", "moderator"),
600 + sample("audio", "member"),
601 + sample("film", "member"),
602 + ];
603 +
604 + for memberships in [&none, &some] {
605 + for base in ["https://mt.example.com", ""] {
606 + assert_eq!(
607 + library_pane_serve(&FORUMS_LIBRARY, memberships, base),
608 + Webview::new().fragment(&library_pane(memberships, base)),
609 + "library: {} memberships, base {base:?}",
610 + memberships.len(),
611 + );
612 + assert_eq!(
613 + settings_pane_serve(&FORUMS_SETTINGS, memberships, base),
614 + Webview::new().fragment(&settings_pane(memberships, base)),
615 + "settings: {} memberships, base {base:?}",
616 + memberships.len(),
617 + );
618 + }
619 + }
620 + }
621 +
572 622 /// Every screen the roster names is checked above.
573 623 ///
574 624 /// The gap this closes is the one a table opens: a screen added to
@@ -583,8 +633,9 @@
583 633 #[test]
584 634 fn every_screen_on_the_seam_is_checked() {
585 635 /// Screens with their own filling test: `/use-cases`, `/fan-plus`,
586 - /// `/c/{username}/{slug}`, `/dashboard/export`, `/git/{owner}`.
587 - const HOLED: usize = 5;
636 + /// `/c/{username}/{slug}`, `/dashboard/export`, `/git/{owner}`, and the
637 + /// two forum panes.
638 + const HOLED: usize = 7;
588 639
589 640 assert_eq!(
590 641 roster().len(),
@@ -211,4 +211,77 @@
211 211 ])),
212 212 ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"anchored\" id=\"git-repos-anchored\" data-menu=\"anchored\" hidden></div></div>")),
213 213 ]);
214 +
215 + pub static FORUMS_LIBRARY: ::quasi_router::stage::Residual =
216 + ::quasi_router::stage::Residual::compiled(&[
217 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div id=\"library-communities\" class=\"region pane\">")),
218 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
219 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"placeholder\" data-state=\"empty\" role=\"status\" aria-live=\"polite\"><p class=\"placeholder-text\">You haven&#39;t joined any forum communities yet.</p>")),
220 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
221 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"placeholder-action\"><a class=\"button\" data-act href=\"")),
222 + ::quasi_router::stage::Op::Hole { scope: 0, id: 0 },
223 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\" target=\"_blank\" rel=\"noopener noreferrer\">Browse Communities</a></div>")),
224 +
225 + ])),
226 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div>")),
227 +
228 + ])),
229 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
230 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"rich\" data-disable-scripting><p>Your memberships across <a href=\"")),
231 + ::quasi_router::stage::Op::Hole { scope: 1, id: 0 },
232 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\" rel=\"noopener noreferrer\">Multithreaded</a> forum communities.</p>\n</div>")),
233 +
234 + ])),
235 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
236 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div role=\"table\" class=\"table\"><div role=\"row\" class=\"table-head\"><span role=\"columnheader\" class=\"table-heading col-Community cell-fill cell-keeps\">Community</span><span role=\"columnheader\" class=\"table-heading col-Role cell-content cell-drops-next\">Role</span><span role=\"columnheader\" class=\"table-heading col-Posts cell-content cell-drops-next\">Posts</span><span role=\"columnheader\" class=\"table-heading col-Joined cell-content cell-drops-first\">Joined</span></div>")),
237 + ::quasi_router::stage::Op::Loop(::std::borrow::Cow::Borrowed(&[
238 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div role=\"row\" class=\"table-row\" data-row><div class=\"cell col-Community cell-fill cell-keeps\"><a class=\"cell-link\" data-act href=\"")),
239 + ::quasi_router::stage::Op::Hole { scope: 2, id: 1 },
240 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\" target=\"_blank\" rel=\"noopener noreferrer\">")),
241 + ::quasi_router::stage::Op::Hole { scope: 2, id: 0 },
242 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</a></div><div class=\"cell col-Role cell-content cell-drops-next\"><span class=\"cell-tokens\"><span class=\"badge\">")),
243 + ::quasi_router::stage::Op::Hole { scope: 2, id: 2 },
244 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</span></span></div><div class=\"cell col-Posts cell-content cell-drops-next cell-value\">")),
245 + ::quasi_router::stage::Op::Hole { scope: 2, id: 3 },
246 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div><div class=\"cell col-Joined cell-content cell-drops-first cell-value\">")),
247 + ::quasi_router::stage::Op::Hole { scope: 2, id: 4 },
248 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div></div>")),
249 +
250 + ])),
251 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div>")),
252 +
253 + ])),
254 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"anchored\" id=\"library-communities-anchored\" data-menu=\"anchored\" hidden></div></div>")),
255 + ]);
256 +
257 + pub static FORUMS_SETTINGS: ::quasi_router::stage::Residual =
258 + ::quasi_router::stage::Residual::compiled(&[
259 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div id=\"settings-forums\" class=\"region pane\"><h2 class=\"heading\">Forum Communities</h2><div class=\"rich\" data-disable-scripting><p>Your memberships across <a href=\"")),
260 + ::quasi_router::stage::Op::Hole { scope: 1, id: 0 },
261 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\" rel=\"noopener noreferrer\">Multithreaded</a> forum communities.</p>\n</div>")),
262 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
263 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"placeholder\" data-state=\"empty\" role=\"status\" aria-live=\"polite\"><p class=\"placeholder-text\">You haven&#39;t joined any forum communities yet.</p></div>")),
264 +
265 + ])),
266 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
267 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div role=\"table\" class=\"table\"><div role=\"row\" class=\"table-head\"><span role=\"columnheader\" class=\"table-heading col-Community cell-fill cell-keeps\">Community</span><span role=\"columnheader\" class=\"table-heading col-Role cell-content cell-drops-next\">Role</span><span role=\"columnheader\" class=\"table-heading col-Posts cell-content cell-drops-next\">Posts</span><span role=\"columnheader\" class=\"table-heading col-Joined cell-content cell-drops-first\">Joined</span></div>")),
268 + ::quasi_router::stage::Op::Loop(::std::borrow::Cow::Borrowed(&[
269 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div role=\"row\" class=\"table-row\" data-row><div class=\"cell col-Community cell-fill cell-keeps\"><a class=\"cell-link\" data-act href=\"")),
270 + ::quasi_router::stage::Op::Hole { scope: 2, id: 1 },
271 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\" target=\"_blank\" rel=\"noopener noreferrer\">")),
272 + ::quasi_router::stage::Op::Hole { scope: 2, id: 0 },
273 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</a></div><div class=\"cell col-Role cell-content cell-drops-next\"><span class=\"cell-tokens\"><span class=\"badge\">")),
274 + ::quasi_router::stage::Op::Hole { scope: 2, id: 2 },
275 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</span></span></div><div class=\"cell col-Posts cell-content cell-drops-next cell-value\">")),
276 + ::quasi_router::stage::Op::Hole { scope: 2, id: 3 },
277 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div><div class=\"cell col-Joined cell-content cell-drops-first cell-value\">")),
278 + ::quasi_router::stage::Op::Hole { scope: 2, id: 4 },
279 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div></div>")),
280 +
281 + ])),
282 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div>")),
283 +
284 + ])),
285 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"anchored\" id=\"settings-forums-anchored\" data-menu=\"anchored\" hidden></div></div>")),
286 + ]);
214 287