Skip to main content

max / makenotwork

Put the repository listing on the residual seam Seven of seventeen. /git/{owner} is the first screen on the seam behind a rate limiter, and the layer goes on at the mount exactly as it does for the adapted mounts beside it, since `served_document_mount` answers an `axum::Router` too. The module header claimed that was the whole of the supposed blocker; it was. Three things the conversion turned up, all of them rules rather than repairs. **A derivation renders combinations the screen cannot produce.** It varies one guard at a time to read each span, so a guarded `Visibility` column and a guarded visibility cell -- two conditionals both reading ownership -- get rendered column-absent with the cell present, and a cell naming a column that is not there is a cell silently lost. The module already noted nothing checked those two agreed. Something does now, so the listing splits into `owner_listing` and `listing` and ownership is asked once, in `page_region`. The rule the seam imposes is that a screen's guards are independent. **A `rich` member is staged through its source, not around it.** `own_prose` takes the markdown as one parameter, so an `include` that formats the string first hands the derivation one sentinel where the document should be, and the fenced clone-URL block came back as a literal ``` inside a `<p>`. Written as a `rich` in the staged shape the account's name is a hole inside the source, docengine parses the fence with a sentinel in it, and the residual holds `<pre><code>` around a gap. Same markup. `own_prose` carries the rule. **A tag is staged through its constructor.** `token Tag::badge(word)` has no sentinel for the `Tag`, and staging the call as a value fails with `Tag: Fill is not satisfied`. Its argument does have one, so quasi-declare now keeps the call in a structured slot and stages what it is given: the badge's markup is compiled and only the word is a hole.
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:01 UTC
Commit: f16d222a95febaeb7d0cecfa92297d246ce5ef69
Parent: dac6ffd
4 files changed, +280 insertions, -51 deletions
@@ -48,14 +48,14 @@
48 48 const EMPTY: &str = "git-repos-empty";
49 49
50 50 /// Everything the screen draws, resolved before it is drawn.
51 - struct Loaded {
51 + pub(crate) struct Loaded {
52 52 owner: String,
53 53 is_owner: bool,
54 54 repos: Vec<Repo>,
55 55 }
56 56
57 57 /// One repository, as the listing shows it.
58 - struct Repo {
58 + pub(crate) struct Repo {
59 59 name: String,
60 60 description: String,
61 61 /// `None` unless the reader owns the account and the repository is not
@@ -87,6 +87,14 @@
87 87 Ok(page_screen(&loaded).into())
88 88 }
89 89
90 + /// The one read this page makes, for the mount that serves it from a residual.
91 + pub(crate) fn reading(
92 + viewer: &super::Viewer,
93 + carried: &super::Carried,
94 + ) -> Result<Loaded, RouteError> {
95 + load(viewer, carried.capture("owner")?)
96 + }
97 +
90 98 /// Resolve the account and the repositories this reader may see.
91 99 fn load(viewer: &super::Viewer, owner: &str) -> Result<Loaded, RouteError> {
92 100 let missing = || RouteError::not_found("no such account");
@@ -129,7 +137,7 @@
129 137
130 138 declare! {
131 139 /// The whole document: the title, the measure, the body.
132 - shape page_screen(loaded: &Loaded) -> Screen;
140 + pub(crate) shape page_screen(loaded: &Loaded) -> Screen;
133 141
134 142 let heading = "{loaded.owner}'s Repositories";
135 143
@@ -137,11 +145,20 @@
137 145 measured MEASURE;
138 146 documented Document::default().classed(crate::shell::body_class(MEASURE, &[]));
139 147
140 - region PAGE_REGION as Pane {
141 - page heading.clone();
142 - include empty(loaded.is_owner, &loaded.owner) when loaded.repos.is_empty();
143 - include listing(loaded) unless loaded.repos.is_empty();
144 - }
148 + include page_region(loaded);
149 + }
150 + }
151 +
152 + declare! {
153 + /// The page's one region, split out so it can be staged.
154 + #[staged]
155 + pub(crate) shape page_region(loaded: &Loaded) -> Slot;
156 +
157 + region PAGE_REGION as Pane {
158 + page "{loaded.owner}'s Repositories";
159 + include empty(loaded.is_owner, &loaded.owner) when loaded.repos.is_empty();
160 + include owner_listing(loaded) when loaded.is_owner and not loaded.repos.is_empty();
161 + include listing(loaded) unless loaded.is_owner or loaded.repos.is_empty();
145 162 }
146 163 }
147 164
@@ -152,24 +169,34 @@
152 169 /// The shipped template made the same split and it is worth keeping: `git
153 170 /// remote add` is not advice a stranger can take.
154 171 ///
155 - /// `own_prose` rather than `rich`, and the interpolation is the reason to
156 - /// say why: `owner` is a `Username`, which `validate_username` restricts to
157 - /// letters, digits and underscores, so it cannot carry markup into a source
158 - /// the renderer no longer hardens. A value that could would want `rich`
159 - /// instead, whatever else is in the string.
172 + /// # The prose is written here rather than passed to `own_prose`
173 + ///
174 + /// `own_prose` takes the markdown **source** as one parameter, so a caller
175 + /// that formats the string first hands a staged shape one sentinel where
176 + /// the whole document should be: the derivation renders `<p>ZQH...HQZ</p>`
177 + /// and the filler writes the request's markdown into that paragraph
178 + /// unparsed. See [`super::own_prose`], where the rule is.
179 + ///
180 + /// Said here, the account's name is a hole **inside** the source, so the
181 + /// derivation parses the fence with a sentinel in it and the residual holds
182 + /// docengine's `<pre><code>` around a gap. Same markup as before, and the
183 + /// two lines that are `own_prose`'s body are inlined with it.
184 + #[staged]
160 185 shape empty(is_owner: bool, owner: &str) -> Slot;
161 186
162 187 region EMPTY as Group {
163 188 empty "No repositories yet.";
164 - include super::own_prose(
165 - "Push a new repository:\n\n```\ngit remote add origin \
166 - https://makenot.work/git/{owner}/my-repo.git\ngit push -u origin main\n```"
167 - ) when is_owner;
189 + rich "Push a new repository:\n\n```\ngit remote add origin \
190 + https://makenot.work/git/{owner}/my-repo.git\ngit push -u origin main\n```"
191 + when is_owner
192 + {
193 + trust quasi_router::Trust::Trusted;
194 + }
168 195 }
169 196 }
170 197
171 198 declare! {
172 - /// The repositories, as a table.
199 + /// The repositories, as a table, in the shape this reader is owed.
173 200 ///
174 201 /// The template drew a `<ul>` of two-line entries. As a table the
175 202 /// description says which column carries the identity and which can be
@@ -177,11 +204,64 @@
177 204 /// however it wraps.
178 205 ///
179 206 /// Nobody but the owner is shown a visibility column, because for everybody
180 - /// else every row in it would say the same word. The visibility cell names
181 - /// its column rather than counting to it, so the guard above is the only
182 - /// place ownership is decided: built by position, this row and that column
183 - /// list were two conditionals that had to agree, and nothing checked that
184 - /// they did.
207 + /// else every row in it would say the same word.
208 + ///
209 + /// # One question, asked once, and the seam is what insisted
210 + ///
211 + /// This was one table with a guarded `Visibility` column and a guarded
212 + /// visibility cell, both reading ownership. That is two conditionals that
213 + /// have to agree, and the module used to note that nothing checked they
214 + /// did. Something does now: **a derivation renders combinations the screen
215 + /// cannot produce.** It varies one guard at a time to read each span, so it
216 + /// renders the column absent with the cell present, and a cell naming a
217 + /// column that is not there is a cell silently lost -- which the renderer
218 + /// panics on rather than dropping.
219 + ///
220 + /// So the rule the seam imposes is that a screen's guards are independent,
221 + /// and two that must agree are one question written twice. Here the
222 + /// question is ownership and it is asked in [`page_region`], which picks
223 + /// the table rather than patching one. The two column lists repeat two
224 + /// lines and buy back a coupling nothing was holding.
225 + #[staged]
226 + shape owner_listing(loaded: &Loaded) -> Node;
227 +
228 + table {
229 + column "Repository" {
230 + width Content;
231 + priority Essential;
232 + }
233 + column "Description" {
234 + width Fill;
235 + }
236 + column "Visibility" {
237 + width Content;
238 + }
239 +
240 + for repo in loaded.repos.iter() {
241 + cells {
242 + cell at "Repository" repo.name.clone();
243 + cell at "Description" repo.description.clone();
244 + // Absent for a public repository, which is the owner's common
245 + // case and the reason the column is theirs alone. The guard is
246 + // on the cell rather than on the token because a token is a
247 + // setting on the cell, and a staged shape cannot guard one: a
248 + // setting renders in its container's opening tag, so the branch
249 + // it makes is not where the declaration wrote it.
250 + cell at "Visibility" "" when repo.visibility.is_some() {
251 + token Tag::badge(repo.visibility_label());
252 + }
253 +
254 + activate to get "/git/{loaded.owner}/{repo.name}" navigating;
255 + }
256 + }
257 + }
258 + }
259 +
260 + declare! {
261 + /// The same listing for everybody else, which is the public set with no
262 + /// visibility to report. See [`owner_listing`] for why this is a second
263 + /// shape rather than a guard.
264 + #[staged]
185 265 shape listing(loaded: &Loaded) -> Node;
186 266
187 267 table {
@@ -192,17 +272,11 @@
192 272 column "Description" {
193 273 width Fill;
194 274 }
195 - column "Visibility" when loaded.is_owner {
196 - width Content;
197 - }
198 275
199 276 for repo in loaded.repos.iter() {
200 277 cells {
201 278 cell at "Repository" repo.name.clone();
202 279 cell at "Description" repo.description.clone();
203 - cell at "Visibility" "" when repo.visibility.is_some() {
204 - token Tag::badge(repo.visibility_label());
205 - }
206 280
207 281 activate to get "/git/{loaded.owner}/{repo.name}" navigating;
208 282 }
@@ -220,23 +294,30 @@
220 294 )))
221 295 }
222 296
297 + /// An account as the tests draw it, with `count` repositories.
298 + ///
299 + /// Module-level rather than inside `mod tests` because `quasi::residuals` needs
300 + /// one too, and `Loaded` is this module's own type. Test-only.
301 + #[cfg(test)]
302 + pub(crate) fn sample(count: usize, is_owner: bool) -> Loaded {
303 + Loaded {
304 + owner: "ada".into(),
305 + is_owner,
306 + repos: (0..count)
307 + .map(|n| Repo {
308 + name: format!("repo{n}"),
309 + description: format!("Number {n}"),
310 + visibility: (is_owner && n == 0).then(|| "private".to_string()),
311 + })
312 + .collect(),
313 + }
314 + }
315 +
223 316 #[cfg(test)]
224 317 mod tests {
225 318 use super::*;
226 319
227 - fn loaded(count: usize, is_owner: bool) -> Loaded {
228 - Loaded {
229 - owner: "ada".into(),
230 - is_owner,
231 - repos: (0..count)
232 - .map(|n| Repo {
233 - name: format!("repo{n}"),
234 - description: format!("Number {n}"),
235 - visibility: (is_owner && n == 0).then(|| "private".to_string()),
236 - })
237 - .collect(),
238 - }
239 - }
320 + use super::sample as loaded;
240 321
241 322 fn html(loaded: &Loaded) -> String {
242 323 use quasi_axum::Serves as _;
@@ -771,15 +771,29 @@
771 771 ),
772 772 )),
773 773 ),
774 + // The first screen on the seam behind a rate limiter, and the layer
775 + // goes on here exactly as it does for the adapted mounts beside it:
776 + // `served_document_mount` answers an `axum::Router` too.
774 777 (
775 778 git_repos::PATH,
776 - public_document_mount(app, git_repos::PATH, git_repos::screen, git_repos::renderer)
777 - .layer(tower_governor::GovernorLayer::new(
778 - crate::helpers::rate_limiter_ms(
779 - crate::constants::GIT_BROWSE_RATE_LIMIT_MS,
780 - crate::constants::GIT_BROWSE_RATE_LIMIT_BURST,
781 - ),
782 - )),
779 + served_document_mount(
780 + app,
781 + git_repos::PATH,
782 + git_repos::renderer,
783 + |viewer, carried| {
784 + let loaded = git_repos::reading(viewer, carried)?;
785 + Ok(Served {
786 + screen: git_repos::page_screen(&loaded),
787 + markup: git_repos::page_region_serve(&residuals::GIT_REPOS, &loaded).into(),
788 + })
789 + },
790 + )
791 + .layer(tower_governor::GovernorLayer::new(
792 + crate::helpers::rate_limiter_ms(
793 + crate::constants::GIT_BROWSE_RATE_LIMIT_MS,
794 + crate::constants::GIT_BROWSE_RATE_LIMIT_BURST,
795 + ),
796 + )),
783 797 ),
784 798 ]
785 799 }
@@ -1113,6 +1127,31 @@
1113 1127 /// be a literal in this repository, or interpolated from a value that
1114 1128 /// cannot carry markup. A creator's description reaching a screen through
1115 1129 /// the database is `Node::rich` however well-behaved it has been.
1130 + /// # Constant, and deliberately not staged
1131 + ///
1132 + /// An `include` of this with a literal folds to the `#[constant]` shim and
1133 + /// is derived once, which is what `/policy` uses six times over.
1134 + ///
1135 + /// `#[staged]` was tried and is wrong. The parameter is the **markdown
1136 + /// source**, so staging replaces the whole document with one sentinel: the
1137 + /// derivation renders `<p>ZQH...HQZ</p>` and the filler writes the request's
1138 + /// markdown into that paragraph unparsed. `git_repos` is where it showed --
1139 + /// a fenced code block came back as a literal ``` inside a `<p>`.
1140 + ///
1141 + /// # A staged screen that wants a value inside prose writes the `rich`
1142 + ///
1143 + /// Not because prose cannot be staged, but because the hole has to be in
1144 + /// the **source** rather than around it. `rich "... [name]({base}) ..."`
1145 + /// written inside the staged shape puts a sentinel in the markdown, so
1146 + /// docengine parses it and the residual holds the markup with the value's
1147 + /// own gap in it; the same sentence formatted first and handed here does
1148 + /// not. `git_repos` and `forum_memberships` are the two sites, and each
1149 + /// inlines this shape's two-line body with a note saying so.
1150 + ///
1151 + /// What a staged `rich` then requires is that the value cannot change how
1152 + /// the source parses -- which is the rule this shape already carries for a
1153 + /// different reason. A value that could alter the parse could carry markup,
1154 + /// and such a value wants `Node::rich`, untrusted and not on the seam.
1116 1155 #[must_use]
1117 1156 #[constant]
1118 1157 pub shape own_prose(source: impl Into<String>) -> Node;
@@ -78,6 +78,9 @@
78 78 ("EXPORT_PORTAL", |plan| {
79 79 Node::Region(super::export_portal::page_region_staged(plan))
80 80 }),
81 + ("GIT_REPOS", |plan| {
82 + Node::Region(super::git_repos::page_region_staged(plan))
83 + }),
81 84 ]
82 85 }
83 86
@@ -520,6 +523,52 @@
520 523 assert_eq!(loops(EXPORT_PORTAL.ops()), 0, "the cards are unrolled");
521 524 }
522 525
526 + /// The repository listing's residual fills to what the renderer builds.
527 + ///
528 + /// `/git/{owner}` is the first residual holding a **token**: a repository's
529 + /// visibility is a `Tag`, which has no sentinel of its own, so the tag is
530 + /// built with one in it and the residual carries the tag's markup as a
531 + /// literal around a hole (quasi-declare's structured slots).
532 + ///
533 + /// Four shapes, and the two axes are independent. Owner and visitor differ
534 + /// by a whole column as well as by the badges, and empty and populated by
535 + /// which of the two branches is placed at all.
536 + #[test]
537 + fn the_git_repos_residual_fills_to_what_the_renderer_builds() {
538 + use quasi_axum::Serves as _;
539 +
540 + for count in [0, 1, 3] {
541 + for is_owner in [true, false] {
542 + let loaded = crate::quasi::git_repos::sample(count, is_owner);
543 + let filled = crate::quasi::git_repos::page_region_serve(&GIT_REPOS, &loaded);
544 + let rendered = Webview::new()
545 + .fragment(&Node::Region(crate::quasi::git_repos::page_region(&loaded)));
546 +
547 + assert_eq!(filled, rendered, "count={count} is_owner={is_owner}");
548 + }
549 + }
550 + }
551 +
552 + /// The badge's markup is compiled and only its word is a hole.
553 + ///
554 + /// What says the structured slot landed. A `Tag` staged as a value fails to
555 + /// compile; a `Tag` staged through leaves `class="tag"` in the residual's
556 + /// literals with the visibility word written in per row. Finding the markup
557 + /// here is finding that the second thing happened.
558 + #[test]
559 + fn the_git_repos_residual_compiles_its_badges() {
560 + let compiled = format!("{:?}", GIT_REPOS.ops());
561 +
562 + assert!(
563 + compiled.contains("badge"),
564 + "the badge markup is not compiled",
565 + );
566 + assert!(
567 + !compiled.contains("private"),
568 + "a visibility word was baked into a literal",
569 + );
570 + }
571 +
523 572 /// Every screen the roster names is checked above.
524 573 ///
525 574 /// The gap this closes is the one a table opens: a screen added to
@@ -534,8 +583,8 @@
534 583 #[test]
535 584 fn every_screen_on_the_seam_is_checked() {
536 585 /// Screens with their own filling test: `/use-cases`, `/fan-plus`,
537 - /// `/c/{username}/{slug}`, `/dashboard/export`.
538 - const HOLED: usize = 4;
586 + /// `/c/{username}/{slug}`, `/dashboard/export`, `/git/{owner}`.
587 + const HOLED: usize = 5;
539 588
540 589 assert_eq!(
541 590 roster().len(),
@@ -151,4 +151,64 @@
151 151 ])),
152 152 ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"anchored\" id=\"export-cards-anchored\" data-menu=\"anchored\" hidden></div></section><h2 class=\"heading\">About Your Data</h2><p class=\"text\">Your data belongs to you. These exports contain everything we store about your account and content. If you&#39;re planning to delete your account, we recommend downloading your data first.</p><div class=\"anchored\" id=\"export-anchored\" data-menu=\"anchored\" hidden></div></div>")),
153 153 ]);
154 +
155 + pub static GIT_REPOS: ::quasi_router::stage::Residual =
156 + ::quasi_router::stage::Residual::compiled(&[
157 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div id=\"git-repos\" class=\"region pane\"><h1 class=\"heading\">")),
158 + ::quasi_router::stage::Op::Hole { scope: 0, id: 0 },
159 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("&#39;s Repositories</h1>")),
160 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
161 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<section id=\"git-repos-empty\" class=\"region group\"><div class=\"placeholder\" data-state=\"empty\" role=\"status\" aria-live=\"polite\"><p class=\"placeholder-text\">No repositories yet.</p></div>")),
162 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
163 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"rich\" data-disable-scripting><p>Push a new repository:</p>\n<pre><code>git remote add origin https://makenot.work/git/")),
164 + ::quasi_router::stage::Op::Hole { scope: 1, id: 0 },
165 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("/my-repo.git\ngit push -u origin main\n</code></pre>\n</div>")),
166 +
167 + ])),
168 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"anchored\" id=\"git-repos-empty-anchored\" data-menu=\"anchored\" hidden></div></section>")),
169 +
170 + ])),
171 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
172 + ::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-Repository cell-content cell-keeps\">Repository</span><span role=\"columnheader\" class=\"table-heading col-Description cell-fill cell-drops-next\">Description</span><span role=\"columnheader\" class=\"table-heading col-Visibility cell-content cell-drops-next\">Visibility</span></div>")),
173 + ::quasi_router::stage::Op::Loop(::std::borrow::Cow::Borrowed(&[
174 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div role=\"row\" class=\"table-row\" data-row href=\"/git/")),
175 + ::quasi_router::stage::Op::Hole { scope: 2, id: 3 },
176 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("/")),
177 + ::quasi_router::stage::Op::Hole { scope: 2, id: 4 },
178 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\"><div class=\"cell col-Repository cell-content cell-keeps cell-value\">")),
179 + ::quasi_router::stage::Op::Hole { scope: 2, id: 0 },
180 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div><div class=\"cell col-Description cell-fill cell-drops-next cell-value\">")),
181 + ::quasi_router::stage::Op::Hole { scope: 2, id: 1 },
182 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div><div class=\"cell col-Visibility cell-content cell-drops-next\">")),
183 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
184 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<span class=\"cell-tokens\"><span class=\"badge\">")),
185 + ::quasi_router::stage::Op::Hole { scope: 2, id: 2 },
186 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</span></span>")),
187 +
188 + ])),
189 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div></div>")),
190 +
191 + ])),
192 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div>")),
193 +
194 + ])),
195 + ::quasi_router::stage::Op::Branch(::std::borrow::Cow::Borrowed(&[
196 + ::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-Repository cell-content cell-keeps\">Repository</span><span role=\"columnheader\" class=\"table-heading col-Description cell-fill cell-drops-next\">Description</span></div>")),
197 + ::quasi_router::stage::Op::Loop(::std::borrow::Cow::Borrowed(&[
198 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div role=\"row\" class=\"table-row\" data-row href=\"/git/")),
199 + ::quasi_router::stage::Op::Hole { scope: 3, id: 2 },
200 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("/")),
201 + ::quasi_router::stage::Op::Hole { scope: 3, id: 3 },
202 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("\"><div class=\"cell col-Repository cell-content cell-keeps cell-value\">")),
203 + ::quasi_router::stage::Op::Hole { scope: 3, id: 0 },
204 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div><div class=\"cell col-Description cell-fill cell-drops-next cell-value\">")),
205 + ::quasi_router::stage::Op::Hole { scope: 3, id: 1 },
206 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div></div>")),
207 +
208 + ])),
209 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("</div>")),
210 +
211 + ])),
212 + ::quasi_router::stage::Op::Lit(::std::borrow::Cow::Borrowed("<div class=\"anchored\" id=\"git-repos-anchored\" data-menu=\"anchored\" hidden></div></div>")),
213 + ]);
154 214