detached c117990

fix(web): MR description as Markdown, latest tag row inline, no install button (#60)

commit
c1179903253ff4075db07b94eb163628bc5d2851
author
brice · · 2026-10-18 09:06 UTC
committer
bnema · · 2026-10-18 09:06 UTC
parent
ef38ad0
signature
SSH UnverifiedSHA256:VOCA5lYARHcbKDZ7GhTV45VmcjWnJ6ZQIBazj17LivA The key is unknown
46 files · +491 -695
M internal/adapters/web/ASSETS.md +1 -1
@@ -16,7 +16,7 @@ content-hashed names. Nothing is fetched at runtime.
1616 | `static/css/*.css` | native CSS, served as one stylesheet in name order; relative `url(fonts/…)` and `url(icons/…)` become hashed URLs | written here | |
1717 | `static/fonts/atkinson-next.woff2`, `atkinson-next-italic.woff2` | Atkinson Hyperlegible Next, variable weight, latin subset (SIL OFL 1.1, `static/fonts/OFL-next.txt`) | Google Fonts | `18b2a1a39a2fa298b0ba5390aca68462669826c90925656f1c1f6796e0e1bbaf`, `4a5037bfaf6680f40147407407ec09fa42925774bde809a579283d27f9f08106` |
1818 | `static/fonts/atkinson-mono.woff2` | Atkinson Hyperlegible Mono, variable weight, latin subset (SIL OFL 1.1, `static/fonts/OFL-mono.txt`) | Google Fonts | `2706b1ee4f452e744ea91f7e4908cbde9c5d35521bf5ffffc71a382a2de89613` |
19-| `static/icons/*.svg` | interface icons, used as CSS masks; `brand.svg` is a grey picture shown as is. Only the icons in use are copied, one file each: `branch`, `search`, `tag`, `file`, `chevron`, `user`, `key`, `log-in`, `log-out`, `download` are Lucide 1.8 (`git-branch`, `search`, `tag`, `file-text`, `chevron-down`, `user-round`, `key-round`, `log-in`, `log-out`, `download`; ISC, https://lucide.dev/license) | Lucide; `brand.svg` written here | |
19+| `static/icons/*.svg` | interface icons, used as CSS masks; `brand.svg` is a grey picture shown as is. Only the icons in use are copied, one file each: `branch`, `search`, `tag`, `file`, `chevron`, `user`, `key`, `log-in`, `log-out` are Lucide 1.8 (`git-branch`, `search`, `tag`, `file-text`, `chevron-down`, `user-round`, `key-round`, `log-in`, `log-out`; ISC, https://lucide.dev/license) | Lucide; `brand.svg` written here | |
2020 | `static/app/*.png` | icons of the installed web app (manifest, `apple-touch-icon`), rendered from `brand.svg` with resvg; the maskable and Apple icons have the picture in the safe zone on `#181818` | generated here | |
2121 | `static/sw.js` | the service worker of the installed app: a network-only passthrough (no cache), served at `/-/sw.js` with `Service-Worker-Allowed: /` and revalidated on each check (not a hashed asset) | written here | |
2222 | `static/rules.json` | speculation rules: prefetch the light same-origin pages on hover (`Speculation-Rules` header); the heavy ones (tree, blob, commit, commits, compare, refs, raw, MR "since") are excluded, each costs several Git reads on the server | written here | |
@@ -16,7 +16,7 @@ content-hashed names. Nothing is fetched at runtime.
16 | `static/css/*.css` | native CSS, served as one stylesheet in name order; relative `url(fonts/…)` and `url(icons/…)` become hashed URLs | written here | |16 | `static/css/*.css` | native CSS, served as one stylesheet in name order; relative `url(fonts/…)` and `url(icons/…)` become hashed URLs | written here | |
17 | `static/fonts/atkinson-next.woff2`, `atkinson-next-italic.woff2` | Atkinson Hyperlegible Next, variable weight, latin subset (SIL OFL 1.1, `static/fonts/OFL-next.txt`) | Google Fonts | `18b2a1a39a2fa298b0ba5390aca68462669826c90925656f1c1f6796e0e1bbaf`, `4a5037bfaf6680f40147407407ec09fa42925774bde809a579283d27f9f08106` |17 | `static/fonts/atkinson-next.woff2`, `atkinson-next-italic.woff2` | Atkinson Hyperlegible Next, variable weight, latin subset (SIL OFL 1.1, `static/fonts/OFL-next.txt`) | Google Fonts | `18b2a1a39a2fa298b0ba5390aca68462669826c90925656f1c1f6796e0e1bbaf`, `4a5037bfaf6680f40147407407ec09fa42925774bde809a579283d27f9f08106` |
18 | `static/fonts/atkinson-mono.woff2` | Atkinson Hyperlegible Mono, variable weight, latin subset (SIL OFL 1.1, `static/fonts/OFL-mono.txt`) | Google Fonts | `2706b1ee4f452e744ea91f7e4908cbde9c5d35521bf5ffffc71a382a2de89613` |18 | `static/fonts/atkinson-mono.woff2` | Atkinson Hyperlegible Mono, variable weight, latin subset (SIL OFL 1.1, `static/fonts/OFL-mono.txt`) | Google Fonts | `2706b1ee4f452e744ea91f7e4908cbde9c5d35521bf5ffffc71a382a2de89613` |
19-| `static/icons/*.svg` | interface icons, used as CSS masks; `brand.svg` is a grey picture shown as is. Only the icons in use are copied, one file each: `branch`, `search`, `tag`, `file`, `chevron`, `user`, `key`, `log-in`, `log-out`, `download` are Lucide 1.8 (`git-branch`, `search`, `tag`, `file-text`, `chevron-down`, `user-round`, `key-round`, `log-in`, `log-out`, `download`; ISC, https://lucide.dev/license) | Lucide; `brand.svg` written here | |19+| `static/icons/*.svg` | interface icons, used as CSS masks; `brand.svg` is a grey picture shown as is. Only the icons in use are copied, one file each: `branch`, `search`, `tag`, `file`, `chevron`, `user`, `key`, `log-in`, `log-out` are Lucide 1.8 (`git-branch`, `search`, `tag`, `file-text`, `chevron-down`, `user-round`, `key-round`, `log-in`, `log-out`; ISC, https://lucide.dev/license) | Lucide; `brand.svg` written here | |
20 | `static/app/*.png` | icons of the installed web app (manifest, `apple-touch-icon`), rendered from `brand.svg` with resvg; the maskable and Apple icons have the picture in the safe zone on `#181818` | generated here | |20 | `static/app/*.png` | icons of the installed web app (manifest, `apple-touch-icon`), rendered from `brand.svg` with resvg; the maskable and Apple icons have the picture in the safe zone on `#181818` | generated here | |
21 | `static/sw.js` | the service worker of the installed app: a network-only passthrough (no cache), served at `/-/sw.js` with `Service-Worker-Allowed: /` and revalidated on each check (not a hashed asset) | written here | |21 | `static/sw.js` | the service worker of the installed app: a network-only passthrough (no cache), served at `/-/sw.js` with `Service-Worker-Allowed: /` and revalidated on each check (not a hashed asset) | written here | |
22 | `static/rules.json` | speculation rules: prefetch the light same-origin pages on hover (`Speculation-Rules` header); the heavy ones (tree, blob, commit, commits, compare, refs, raw, MR "since") are excluded, each costs several Git reads on the server | written here | |22 | `static/rules.json` | speculation rules: prefetch the light same-origin pages on hover (`Speculation-Rules` header); the heavy ones (tree, blob, commit, commits, compare, refs, raw, MR "since") are excluded, each costs several Git reads on the server | written here | |
M internal/adapters/web/jstest/install.test.mjs +5 -71
@@ -1,22 +1,15 @@
11 // Tests of the install block of static/js/ermite.js (ermiteInstall): the
2-// service worker registration and the "install app" button. Run with
2+// service worker registration. Run with
33 // node --test 'internal/adapters/web/jstest/*.test.mjs'
44 import assert from 'node:assert/strict';
55 import { readFileSync } from 'node:fs';
66 import { test } from 'node:test';
7-import { dom, el, installDom } from './dom.mjs';
7+import { installDom } from './dom.mjs';
88
99 const doc = installDom();
10-const button = el('button', { attrs: { 'data-install': '' } });
11-button.hidden = true;
12-document.body.append(button);
13-
1410 const setNavigator = (v) => Object.defineProperty(globalThis, 'navigator', { value: v, configurable: true, writable: true });
1511 const listeners = {};
1612 globalThis.addEventListener = (type, fn) => { listeners[type] = fn; };
17-const fire = (type, ev) => listeners[type](ev);
18-let standalone = false;
19-globalThis.matchMedia = () => ({ matches: standalone });
2013 doc.readyState = 'loading';
2114 const registered = [];
2215 setNavigator({ serviceWorker: { register: async (url, opts) => { registered.push([url, opts]); return {}; } } });
@@ -25,17 +18,9 @@ setNavigator({ serviceWorker: { register: async (url, opts) => { registered.push
2518 const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8');
2619 const start = src.indexOf(' window.ermiteInstall = ');
2720 const end = src.indexOf(' })();', start) + ' })();'.length;
28-new Function('$$', src.slice(start, end))((sel) => document.querySelectorAll(sel));
21+new Function(src.slice(start, end))();
2922 const I = window.ermiteInstall;
3023
31-// promptEvent is a beforeinstallprompt event.
32-function promptEvent(outcome = 'accepted') {
33- const e = { prevented: false, prompts: 0, preventDefault() { this.prevented = true; } };
34- e.prompt = async () => { e.prompts++; };
35- e.userChoice = Promise.resolve({ outcome });
36- return e;
37-}
38-
3924 test('the service worker is registered at the root scope once the page has loaded', async () => {
4025 assert.equal(registered.length, 0, 'not before load');
4126 await listeners.load();
@@ -49,57 +34,6 @@ test('a refused registration is not an error', async () => {
4934 assert.equal(await I.register(), null);
5035 });
5136
52-test('the button shows on beforeinstallprompt, and a click prompts once', async () => {
53- const e = promptEvent();
54- assert.equal(button.hidden, true);
55- fire('beforeinstallprompt', e);
56- assert.equal(e.prevented, true, 'the browser banner is replaced by the button');
57- assert.equal(button.hidden, false);
58- const done = listeners.click({ target: { closest: (s) => (s === '[data-install]' ? button : null) } });
59- assert.equal(done, undefined);
60- assert.equal(await I.pending, 'accepted');
61- assert.equal(await I.prompt(), '', 'the event was used by the click');
62- assert.equal(e.prompts, 1);
63- assert.equal(button.hidden, true);
64-});
65-
66-test('the button stays while the dialog is up; its focus then moves to the search', async () => {
67- const search = el('button', { attrs: { 'aria-controls': 'panel-cmd' } });
68- document.body.append(search);
69- const e = promptEvent('dismissed');
70- let shownDuring = null;
71- e.prompt = async () => { shownDuring = !button.hidden; };
72- fire('beforeinstallprompt', e);
73- button.focus();
74- dom.focused = [];
75- assert.equal(await I.prompt(), 'dismissed');
76- assert.equal(shownDuring, true, 'not hidden under the dialog');
77- assert.equal(button.hidden, true);
78- assert.deepEqual(dom.focused, [search]);
79- dom.active = null;
80- search.remove();
81-});
82-
83-test('prompt without an event does nothing; a dismissal reports its outcome', async () => {
84- assert.equal(await I.prompt(), '');
85- const e = promptEvent('dismissed');
86- fire('beforeinstallprompt', e);
87- assert.equal(await I.prompt(), 'dismissed');
88- assert.equal(button.hidden, true);
89-});
90-
91-test('appinstalled hides the button', () => {
92- fire('beforeinstallprompt', promptEvent());
93- assert.equal(button.hidden, false);
94- fire('appinstalled', {});
95- assert.equal(button.hidden, true);
96-});
97-
98-test('an app that runs installed never shows the button', () => {
99- standalone = true;
100- const e = promptEvent();
101- fire('beforeinstallprompt', e);
102- assert.equal(button.hidden, true);
103- assert.equal(e.prevented, false);
104- standalone = false;
37+test('the page leaves the browser its own install offer', () => {
38+ assert.equal(listeners.beforeinstallprompt, undefined, 'no preventDefault on beforeinstallprompt');
10539 });
@@ -1,22 +1,15 @@
1 // Tests of the install block of static/js/ermite.js (ermiteInstall): the1 // Tests of the install block of static/js/ermite.js (ermiteInstall): the
2-// service worker registration and the "install app" button. Run with2+// service worker registration. Run with
3 // node --test 'internal/adapters/web/jstest/*.test.mjs'3 // node --test 'internal/adapters/web/jstest/*.test.mjs'
4 import assert from 'node:assert/strict';4 import assert from 'node:assert/strict';
5 import { readFileSync } from 'node:fs';5 import { readFileSync } from 'node:fs';
6 import { test } from 'node:test';6 import { test } from 'node:test';
7-import { dom, el, installDom } from './dom.mjs';7+import { installDom } from './dom.mjs';
8 8
9 const doc = installDom();9 const doc = installDom();
10-const button = el('button', { attrs: { 'data-install': '' } });
11-button.hidden = true;
12-document.body.append(button);
13-
14 const setNavigator = (v) => Object.defineProperty(globalThis, 'navigator', { value: v, configurable: true, writable: true });10 const setNavigator = (v) => Object.defineProperty(globalThis, 'navigator', { value: v, configurable: true, writable: true });
15 const listeners = {};11 const listeners = {};
16 globalThis.addEventListener = (type, fn) => { listeners[type] = fn; };12 globalThis.addEventListener = (type, fn) => { listeners[type] = fn; };
17-const fire = (type, ev) => listeners[type](ev);
18-let standalone = false;
19-globalThis.matchMedia = () => ({ matches: standalone });
20 doc.readyState = 'loading';13 doc.readyState = 'loading';
21 const registered = [];14 const registered = [];
22 setNavigator({ serviceWorker: { register: async (url, opts) => { registered.push([url, opts]); return {}; } } });15 setNavigator({ serviceWorker: { register: async (url, opts) => { registered.push([url, opts]); return {}; } } });
@@ -25,17 +18,9 @@ setNavigator({ serviceWorker: { register: async (url, opts) => { registered.push
25 const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8');18 const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8');
26 const start = src.indexOf(' window.ermiteInstall = ');19 const start = src.indexOf(' window.ermiteInstall = ');
27 const end = src.indexOf(' })();', start) + ' })();'.length;20 const end = src.indexOf(' })();', start) + ' })();'.length;
28-new Function('$$', src.slice(start, end))((sel) => document.querySelectorAll(sel));21+new Function(src.slice(start, end))();
29 const I = window.ermiteInstall;22 const I = window.ermiteInstall;
30 23
31-// promptEvent is a beforeinstallprompt event.
32-function promptEvent(outcome = 'accepted') {
33- const e = { prevented: false, prompts: 0, preventDefault() { this.prevented = true; } };
34- e.prompt = async () => { e.prompts++; };
35- e.userChoice = Promise.resolve({ outcome });
36- return e;
37-}
38-
39 test('the service worker is registered at the root scope once the page has loaded', async () => {24 test('the service worker is registered at the root scope once the page has loaded', async () => {
40 assert.equal(registered.length, 0, 'not before load');25 assert.equal(registered.length, 0, 'not before load');
41 await listeners.load();26 await listeners.load();
@@ -49,57 +34,6 @@ test('a refused registration is not an error', async () => {
49 assert.equal(await I.register(), null);34 assert.equal(await I.register(), null);
50 });35 });
51 36
52-test('the button shows on beforeinstallprompt, and a click prompts once', async () => {37+test('the page leaves the browser its own install offer', () => {
53- const e = promptEvent();38+ assert.equal(listeners.beforeinstallprompt, undefined, 'no preventDefault on beforeinstallprompt');
54- assert.equal(button.hidden, true);
55- fire('beforeinstallprompt', e);
56- assert.equal(e.prevented, true, 'the browser banner is replaced by the button');
57- assert.equal(button.hidden, false);
58- const done = listeners.click({ target: { closest: (s) => (s === '[data-install]' ? button : null) } });
59- assert.equal(done, undefined);
60- assert.equal(await I.pending, 'accepted');
61- assert.equal(await I.prompt(), '', 'the event was used by the click');
62- assert.equal(e.prompts, 1);
63- assert.equal(button.hidden, true);
64-});
65-
66-test('the button stays while the dialog is up; its focus then moves to the search', async () => {
67- const search = el('button', { attrs: { 'aria-controls': 'panel-cmd' } });
68- document.body.append(search);
69- const e = promptEvent('dismissed');
70- let shownDuring = null;
71- e.prompt = async () => { shownDuring = !button.hidden; };
72- fire('beforeinstallprompt', e);
73- button.focus();
74- dom.focused = [];
75- assert.equal(await I.prompt(), 'dismissed');
76- assert.equal(shownDuring, true, 'not hidden under the dialog');
77- assert.equal(button.hidden, true);
78- assert.deepEqual(dom.focused, [search]);
79- dom.active = null;
80- search.remove();
81-});
82-
83-test('prompt without an event does nothing; a dismissal reports its outcome', async () => {
84- assert.equal(await I.prompt(), '');
85- const e = promptEvent('dismissed');
86- fire('beforeinstallprompt', e);
87- assert.equal(await I.prompt(), 'dismissed');
88- assert.equal(button.hidden, true);
89-});
90-
91-test('appinstalled hides the button', () => {
92- fire('beforeinstallprompt', promptEvent());
93- assert.equal(button.hidden, false);
94- fire('appinstalled', {});
95- assert.equal(button.hidden, true);
96-});
97-
98-test('an app that runs installed never shows the button', () => {
99- standalone = true;
100- const e = promptEvent();
101- fire('beforeinstallprompt', e);
102- assert.equal(button.hidden, true);
103- assert.equal(e.prevented, false);
104- standalone = false;
105 });39 });
M internal/adapters/web/manifest_test.go +0 -1
@@ -57,7 +57,6 @@ func TestWebAppManifest(t *testing.T) {
5757 page := renderToString(t, Shell(Layout{Assets: u}, nil))
5858 assert.Contains(t, page, `<link rel="manifest" href="`+u.Manifest+`">`)
5959 assert.Contains(t, page, `<link rel="apple-touch-icon" href="`+u.AppleIcon+`">`)
60- assert.Contains(t, page, `data-install`)
6160 }
6261
6362 // The icons are exactly the size the manifest declares: Chrome does not
@@ -57,7 +57,6 @@ func TestWebAppManifest(t *testing.T) {
57 page := renderToString(t, Shell(Layout{Assets: u}, nil))57 page := renderToString(t, Shell(Layout{Assets: u}, nil))
58 assert.Contains(t, page, `<link rel="manifest" href="`+u.Manifest+`">`)58 assert.Contains(t, page, `<link rel="manifest" href="`+u.Manifest+`">`)
59 assert.Contains(t, page, `<link rel="apple-touch-icon" href="`+u.AppleIcon+`">`)59 assert.Contains(t, page, `<link rel="apple-touch-icon" href="`+u.AppleIcon+`">`)
60- assert.Contains(t, page, `data-install`)
61 }60 }
62 61
63 // The icons are exactly the size the manifest declares: Chrome does not62 // The icons are exactly the size the manifest declares: Chrome does not
M internal/adapters/web/mr.go +14 -2
@@ -5,6 +5,7 @@ package web
55 import (
66 "context"
77 "html"
8+ "html/template"
89 "io"
910 "net/http"
1011 "net/url"
@@ -196,7 +197,9 @@ type MRPage struct {
196197 Opened Person
197198 Updated string
198199 UpdatedAt string
199- Desc []TextPart
200+ // Desc is the description rendered as Markdown (as a README: no raw
201+ // HTML, filtered URLs), "" when it is empty.
202+ Desc template.HTML
200203 Head string
201204 HeadShort string
202205 MergedShort string
@@ -469,7 +472,6 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ
469472 State: mrStateName(m), Open: open, Signed: signed,
470473 Source: visibleText(m.Source), Target: visibleText(m.Target),
471474 Author: s.who(m.Author),
472- Desc: plainParts(m.Description.String()),
473475 Head: head, HeadShort: short(head),
474476 NFiles: res.Stats.FilesChanged,
475477 Adds: res.Stats.Additions,
@@ -477,6 +479,16 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ
477479 Files: diffFiles(res.Diff.Files),
478480 }
479481 contextGaps(p.Files, res.Diff.Files, repoHref(d.name(), "context", head))
482+ if desc := m.Description.String(); strings.TrimSpace(desc) != "" {
483+ // Relative links point into the source branch as it is now (its head
484+ // commit); the heading ids carry their own prefix, apart from the
485+ // ids of the diff.
486+ h, _, err := s.md.renderWithIDs([]byte(desc), d.name(), head, "", "mr-desc-")
487+ if err != nil {
488+ return nil, 0, err
489+ }
490+ p.Desc = h
491+ }
480492 p.Opened.Name = p.Author
481493 p.Opened.When, p.Opened.WhenAt = whenOf(now, m.CreatedAt)
482494 if !m.CreatedAt.IsZero() {
@@ -5,6 +5,7 @@ package web
5 import (5 import (
6 "context"6 "context"
7 "html"7 "html"
8+ "html/template"
8 "io"9 "io"
9 "net/http"10 "net/http"
10 "net/url"11 "net/url"
@@ -196,7 +197,9 @@ type MRPage struct {
196 Opened Person197 Opened Person
197 Updated string198 Updated string
198 UpdatedAt string199 UpdatedAt string
199- Desc []TextPart200+ // Desc is the description rendered as Markdown (as a README: no raw
201+ // HTML, filtered URLs), "" when it is empty.
202+ Desc template.HTML
200 Head string203 Head string
201 HeadShort string204 HeadShort string
202 MergedShort string205 MergedShort string
@@ -469,7 +472,6 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ
469 State: mrStateName(m), Open: open, Signed: signed,472 State: mrStateName(m), Open: open, Signed: signed,
470 Source: visibleText(m.Source), Target: visibleText(m.Target),473 Source: visibleText(m.Source), Target: visibleText(m.Target),
471 Author: s.who(m.Author),474 Author: s.who(m.Author),
472- Desc: plainParts(m.Description.String()),
473 Head: head, HeadShort: short(head),475 Head: head, HeadShort: short(head),
474 NFiles: res.Stats.FilesChanged,476 NFiles: res.Stats.FilesChanged,
475 Adds: res.Stats.Additions,477 Adds: res.Stats.Additions,
@@ -477,6 +479,16 @@ func (s *Server) mrPage(ctx context.Context, r *http.Request, since bool) (templ
477 Files: diffFiles(res.Diff.Files),479 Files: diffFiles(res.Diff.Files),
478 }480 }
479 contextGaps(p.Files, res.Diff.Files, repoHref(d.name(), "context", head))481 contextGaps(p.Files, res.Diff.Files, repoHref(d.name(), "context", head))
482+ if desc := m.Description.String(); strings.TrimSpace(desc) != "" {
483+ // Relative links point into the source branch as it is now (its head
484+ // commit); the heading ids carry their own prefix, apart from the
485+ // ids of the diff.
486+ h, _, err := s.md.renderWithIDs([]byte(desc), d.name(), head, "", "mr-desc-")
487+ if err != nil {
488+ return nil, 0, err
489+ }
490+ p.Desc = h
491+ }
480 p.Opened.Name = p.Author492 p.Opened.Name = p.Author
481 p.Opened.When, p.Opened.WhenAt = whenOf(now, m.CreatedAt)493 p.Opened.When, p.Opened.WhenAt = whenOf(now, m.CreatedAt)
482 if !m.CreatedAt.IsZero() {494 if !m.CreatedAt.IsZero() {
M internal/adapters/web/mr.templ +5 -3
@@ -140,8 +140,10 @@ templ MergeRequest(l Layout, p MRPage) {
140140 </dl>
141141 </header>
142142 @LiveSlot("mr-notice")
143- if len(p.Desc) > 0 {
144- <p class="mr-desc">@userText(p.Desc)</p>
143+ if p.Desc != "" {
144+ <article class="mr-desc prose" aria-label="description" hx-ignore>
145+ @templ.Raw(string(p.Desc))
146+ </article>
145147 }
146148 if p.Since != nil {
147149 <nav class="since" aria-label="Diff range">
@@ -481,7 +483,7 @@ templ Compare(l Layout, p ComparePage) {
481483 <label for="mr-title">title</label>
482484 <input id="mr-title" name="title" type="text" maxlength="120" required value={ p.Create.Title } autocomplete="off"/>
483485 <label for="mr-desc">description</label>
484- <textarea id="mr-desc" name="description" rows="4" maxlength="8000" placeholder="plain text, optional"></textarea>
486+ <textarea id="mr-desc" name="description" rows="4" maxlength="8000" placeholder="Markdown, optional"></textarea>
485487 <div class="f-btns">
486488 <button type="submit" class="btn primary">Create merge request</button>
487489 </div>
@@ -140,8 +140,10 @@ templ MergeRequest(l Layout, p MRPage) {
140 </dl>140 </dl>
141 </header>141 </header>
142 @LiveSlot("mr-notice")142 @LiveSlot("mr-notice")
143- if len(p.Desc) > 0 {143+ if p.Desc != "" {
144- <p class="mr-desc">@userText(p.Desc)</p>144+ <article class="mr-desc prose" aria-label="description" hx-ignore>
145+ @templ.Raw(string(p.Desc))
146+ </article>
145 }147 }
146 if p.Since != nil {148 if p.Since != nil {
147 <nav class="since" aria-label="Diff range">149 <nav class="since" aria-label="Diff range">
@@ -481,7 +483,7 @@ templ Compare(l Layout, p ComparePage) {
481 <label for="mr-title">title</label>483 <label for="mr-title">title</label>
482 <input id="mr-title" name="title" type="text" maxlength="120" required value={ p.Create.Title } autocomplete="off"/>484 <input id="mr-title" name="title" type="text" maxlength="120" required value={ p.Create.Title } autocomplete="off"/>
483 <label for="mr-desc">description</label>485 <label for="mr-desc">description</label>
484- <textarea id="mr-desc" name="description" rows="4" maxlength="8000" placeholder="plain text, optional"></textarea>486+ <textarea id="mr-desc" name="description" rows="4" maxlength="8000" placeholder="Markdown, optional"></textarea>
485 <div class="f-btns">487 <div class="f-btns">
486 <button type="submit" class="btn primary">Create merge request</button>488 <button type="submit" class="btn primary">Create merge request</button>
487 </div>489 </div>
M internal/adapters/web/mr_templ.go +100 -100 generated file: collapsed
M internal/adapters/web/pages.templ +3 -2
@@ -68,10 +68,11 @@ templ latestTagFact(t LatestTag) {
6868 <dt>latest tag</dt>
6969 <dd class="latest-tag">
7070 if t.Name != "" {
71- <a class="tag-name" href={ templ.SafeURL(t.Href) } title={ t.Subject }><span class="ico i-tag" aria-hidden="true"></span>{ t.Name }</a>
71+ <a class="tag-name" href={ templ.SafeURL(t.Href) } title={ t.Subject }>{ t.Name }</a>
7272 if t.WhenAt != "" {
73- <time class="dim" datetime={ t.WhenAt } title={ t.WhenAt }>{ t.When }</time>
73+ { " · " }<time datetime={ t.WhenAt } title={ t.WhenAt }>{ t.When }</time>
7474 }
75+ { " · " }
7576 }
7677 <a class="all-tags" href={ templ.SafeURL(t.RefsHref) }>{ plural(t.Count, "tag", "tags") }</a>
7778 </dd>
@@ -68,10 +68,11 @@ templ latestTagFact(t LatestTag) {
68 <dt>latest tag</dt>68 <dt>latest tag</dt>
69 <dd class="latest-tag">69 <dd class="latest-tag">
70 if t.Name != "" {70 if t.Name != "" {
71- <a class="tag-name" href={ templ.SafeURL(t.Href) } title={ t.Subject }><span class="ico i-tag" aria-hidden="true"></span>{ t.Name }</a>71+ <a class="tag-name" href={ templ.SafeURL(t.Href) } title={ t.Subject }>{ t.Name }</a>
72 if t.WhenAt != "" {72 if t.WhenAt != "" {
73- <time class="dim" datetime={ t.WhenAt } title={ t.WhenAt }>{ t.When }</time>73+ { " · " }<time datetime={ t.WhenAt } title={ t.WhenAt }>{ t.When }</time>
74 }74 }
75+ { " · " }
75 }76 }
76 <a class="all-tags" href={ templ.SafeURL(t.RefsHref) }>{ plural(t.Count, "tag", "tags") }</a>77 <a class="all-tags" href={ templ.SafeURL(t.RefsHref) }>{ plural(t.Count, "tag", "tags") }</a>
77 </dd>78 </dd>
M internal/adapters/web/pages_templ.go +325 -299
@@ -266,14 +266,14 @@ func latestTagFact(t LatestTag) templ.Component {
266266 if templ_7745c5c3_Err != nil {
267267 return templ_7745c5c3_Err
268268 }
269- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"><span class=\"ico i-tag\" aria-hidden=\"true\"></span>")
269+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
270270 if templ_7745c5c3_Err != nil {
271271 return templ_7745c5c3_Err
272272 }
273273 var templ_7745c5c3_Var12 string
274274 templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(t.Name)
275275 if templ_7745c5c3_Err != nil {
276- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 71, Col: 132}
276+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 71, Col: 82}
277277 }
278278 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
279279 if templ_7745c5c3_Err != nil {
@@ -284,78 +284,104 @@ func latestTagFact(t LatestTag) templ.Component {
284284 return templ_7745c5c3_Err
285285 }
286286 if t.WhenAt != "" {
287- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<time class=\"dim\" datetime=\"")
288- if templ_7745c5c3_Err != nil {
289- return templ_7745c5c3_Err
290- }
291287 var templ_7745c5c3_Var13 string
292- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)
288+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
293289 if templ_7745c5c3_Err != nil {
294- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 41}
290+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 12}
295291 }
296- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
292+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
297293 if templ_7745c5c3_Err != nil {
298294 return templ_7745c5c3_Err
299295 }
300- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" title=\"")
296+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<time datetime=\"")
301297 if templ_7745c5c3_Err != nil {
302298 return templ_7745c5c3_Err
303299 }
304300 var templ_7745c5c3_Var14 string
305301 templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)
306302 if templ_7745c5c3_Err != nil {
307- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 60}
303+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 39}
308304 }
309305 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
310306 if templ_7745c5c3_Err != nil {
311307 return templ_7745c5c3_Err
312308 }
313- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")
309+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" title=\"")
314310 if templ_7745c5c3_Err != nil {
315311 return templ_7745c5c3_Err
316312 }
317313 var templ_7745c5c3_Var15 string
318- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(t.When)
314+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)
315+ if templ_7745c5c3_Err != nil {
316+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 58}
317+ }
318+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
319319 if templ_7745c5c3_Err != nil {
320- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 71}
320+ return templ_7745c5c3_Err
321+ }
322+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")
323+ if templ_7745c5c3_Err != nil {
324+ return templ_7745c5c3_Err
325+ }
326+ var templ_7745c5c3_Var16 string
327+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(t.When)
328+ if templ_7745c5c3_Err != nil {
329+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 69}
321330 }
322- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
331+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
323332 if templ_7745c5c3_Err != nil {
324333 return templ_7745c5c3_Err
325334 }
326- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</time> ")
335+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</time>")
327336 if templ_7745c5c3_Err != nil {
328337 return templ_7745c5c3_Err
329338 }
330339 }
340+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ")
341+ if templ_7745c5c3_Err != nil {
342+ return templ_7745c5c3_Err
343+ }
344+ var templ_7745c5c3_Var17 string
345+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
346+ if templ_7745c5c3_Err != nil {
347+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 75, Col: 11}
348+ }
349+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
350+ if templ_7745c5c3_Err != nil {
351+ return templ_7745c5c3_Err
352+ }
353+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ")
354+ if templ_7745c5c3_Err != nil {
355+ return templ_7745c5c3_Err
356+ }
331357 }
332- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<a class=\"all-tags\" href=\"")
358+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<a class=\"all-tags\" href=\"")
333359 if templ_7745c5c3_Err != nil {
334360 return templ_7745c5c3_Err
335361 }
336- var templ_7745c5c3_Var16 templ.SafeURL
337- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(t.RefsHref))
362+ var templ_7745c5c3_Var18 templ.SafeURL
363+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(t.RefsHref))
338364 if templ_7745c5c3_Err != nil {
339- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 76, Col: 54}
365+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 77, Col: 54}
340366 }
341- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
367+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
342368 if templ_7745c5c3_Err != nil {
343369 return templ_7745c5c3_Err
344370 }
345- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\">")
371+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\">")
346372 if templ_7745c5c3_Err != nil {
347373 return templ_7745c5c3_Err
348374 }
349- var templ_7745c5c3_Var17 string
350- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(plural(t.Count, "tag", "tags"))
375+ var templ_7745c5c3_Var19 string
376+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(plural(t.Count, "tag", "tags"))
351377 if templ_7745c5c3_Err != nil {
352- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 76, Col: 89}
378+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 77, Col: 89}
353379 }
354- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
380+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
355381 if templ_7745c5c3_Err != nil {
356382 return templ_7745c5c3_Err
357383 }
358- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</a></dd>")
384+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</a></dd>")
359385 if templ_7745c5c3_Err != nil {
360386 return templ_7745c5c3_Err
361387 }
@@ -379,12 +405,12 @@ func summaryRail(p SummaryPage) templ.Component {
379405 }()
380406 }
381407 ctx = templ.InitializeContext(ctx)
382- templ_7745c5c3_Var18 := templ.GetChildren(ctx)
383- if templ_7745c5c3_Var18 == nil {
384- templ_7745c5c3_Var18 = templ.NopComponent
408+ templ_7745c5c3_Var20 := templ.GetChildren(ctx)
409+ if templ_7745c5c3_Var20 == nil {
410+ templ_7745c5c3_Var20 = templ.NopComponent
385411 }
386412 ctx = templ.ClearChildren(ctx)
387- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<section aria-labelledby=\"r-clone\"><h2 id=\"r-clone\">Clone</h2>")
413+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<section aria-labelledby=\"r-clone\"><h2 id=\"r-clone\">Clone</h2>")
388414 if templ_7745c5c3_Err != nil {
389415 return templ_7745c5c3_Err
390416 }
@@ -392,25 +418,25 @@ func summaryRail(p SummaryPage) templ.Component {
392418 if templ_7745c5c3_Err != nil {
393419 return templ_7745c5c3_Err
394420 }
395- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</section><section aria-labelledby=\"r-facts\"><h2 id=\"r-facts\">About</h2><dl class=\"kv\">")
421+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</section><section aria-labelledby=\"r-facts\"><h2 id=\"r-facts\">About</h2><dl class=\"kv\">")
396422 if templ_7745c5c3_Err != nil {
397423 return templ_7745c5c3_Err
398424 }
399425 for _, f := range p.Facts {
400- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<dt>")
426+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<dt>")
401427 if templ_7745c5c3_Err != nil {
402428 return templ_7745c5c3_Err
403429 }
404- var templ_7745c5c3_Var19 string
405- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(f.Key)
430+ var templ_7745c5c3_Var21 string
431+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(f.Key)
406432 if templ_7745c5c3_Err != nil {
407- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 87, Col: 15}
433+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 88, Col: 15}
408434 }
409- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
435+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
410436 if templ_7745c5c3_Err != nil {
411437 return templ_7745c5c3_Err
412438 }
413- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</dt>")
439+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</dt>")
414440 if templ_7745c5c3_Err != nil {
415441 return templ_7745c5c3_Err
416442 }
@@ -425,25 +451,25 @@ func summaryRail(p SummaryPage) templ.Component {
425451 return templ_7745c5c3_Err
426452 }
427453 }
428- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</dl></section>")
454+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</dl></section>")
429455 if templ_7745c5c3_Err != nil {
430456 return templ_7745c5c3_Err
431457 }
432458 if len(p.Recent) > 0 {
433- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<section aria-labelledby=\"r-recent\"><h2 id=\"r-recent\">Recent commits <a href=\"")
459+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<section aria-labelledby=\"r-recent\"><h2 id=\"r-recent\">Recent commits <a href=\"")
434460 if templ_7745c5c3_Err != nil {
435461 return templ_7745c5c3_Err
436462 }
437- var templ_7745c5c3_Var20 templ.SafeURL
438- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.AllHref))
463+ var templ_7745c5c3_Var22 templ.SafeURL
464+ templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.AllHref))
439465 if templ_7745c5c3_Err != nil {
440- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 96, Col: 105}
466+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 97, Col: 105}
441467 }
442- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
468+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
443469 if templ_7745c5c3_Err != nil {
444470 return templ_7745c5c3_Err
445471 }
446- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">View all</a></h2>")
472+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\">View all</a></h2>")
447473 if templ_7745c5c3_Err != nil {
448474 return templ_7745c5c3_Err
449475 }
@@ -451,7 +477,7 @@ func summaryRail(p SummaryPage) templ.Component {
451477 if templ_7745c5c3_Err != nil {
452478 return templ_7745c5c3_Err
453479 }
454- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</section>")
480+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</section>")
455481 if templ_7745c5c3_Err != nil {
456482 return templ_7745c5c3_Err
457483 }
@@ -463,7 +489,7 @@ func summaryRail(p SummaryPage) templ.Component {
463489 }
464490 }
465491 if len(p.Outline) > 0 && p.Readme != nil {
466- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<section aria-labelledby=\"r-outline\"><h2 id=\"r-outline\">On this page</h2>")
492+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<section aria-labelledby=\"r-outline\"><h2 id=\"r-outline\">On this page</h2>")
467493 if templ_7745c5c3_Err != nil {
468494 return templ_7745c5c3_Err
469495 }
@@ -471,7 +497,7 @@ func summaryRail(p SummaryPage) templ.Component {
471497 if templ_7745c5c3_Err != nil {
472498 return templ_7745c5c3_Err
473499 }
474- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</section>")
500+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</section>")
475501 if templ_7745c5c3_Err != nil {
476502 return templ_7745c5c3_Err
477503 }
@@ -497,12 +523,12 @@ func Refs(l Layout, p RefsPage) templ.Component {
497523 }()
498524 }
499525 ctx = templ.InitializeContext(ctx)
500- templ_7745c5c3_Var21 := templ.GetChildren(ctx)
501- if templ_7745c5c3_Var21 == nil {
502- templ_7745c5c3_Var21 = templ.NopComponent
526+ templ_7745c5c3_Var23 := templ.GetChildren(ctx)
527+ if templ_7745c5c3_Var23 == nil {
528+ templ_7745c5c3_Var23 = templ.NopComponent
503529 }
504530 ctx = templ.ClearChildren(ctx)
505- templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
531+ templ_7745c5c3_Var24 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
506532 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
507533 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
508534 if !templ_7745c5c3_IsBuffer {
@@ -514,7 +540,7 @@ func Refs(l Layout, p RefsPage) templ.Component {
514540 }()
515541 }
516542 ctx = templ.InitializeContext(ctx)
517- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"filter\"><label for=\"ref-filter\" class=\"sr\">filter</label> <input id=\"ref-filter\" type=\"search\" data-filter=\"#ref-rows > tr\" placeholder=\"Filter branches and tags…\" autocomplete=\"off\" hx-on:input=\"ermite.filter(this)\"></div><table class=\"list refs\" aria-label=\"Branches and tags\"><thead><tr><th class=\"cur\"></th><th class=\"c-kind\">kind</th><th class=\"c-name\">name</th><th class=\"c-sha\">commit</th><th class=\"c-sub\">subject</th><th class=\"c-when\">date</th></tr></thead>")
543+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div class=\"filter\"><label for=\"ref-filter\" class=\"sr\">filter</label> <input id=\"ref-filter\" type=\"search\" data-filter=\"#ref-rows > tr\" placeholder=\"Filter branches and tags…\" autocomplete=\"off\" hx-on:input=\"ermite.filter(this)\"></div><table class=\"list refs\" aria-label=\"Branches and tags\"><thead><tr><th class=\"cur\"></th><th class=\"c-kind\">kind</th><th class=\"c-name\">name</th><th class=\"c-sha\">commit</th><th class=\"c-sub\">subject</th><th class=\"c-when\">date</th></tr></thead>")
518544 if templ_7745c5c3_Err != nil {
519545 return templ_7745c5c3_Err
520546 }
@@ -522,32 +548,32 @@ func Refs(l Layout, p RefsPage) templ.Component {
522548 if templ_7745c5c3_Err != nil {
523549 return templ_7745c5c3_Err
524550 }
525- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</table>")
551+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</table>")
526552 if templ_7745c5c3_Err != nil {
527553 return templ_7745c5c3_Err
528554 }
529555 if p.Note != "" {
530- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<p class=\"note\">")
556+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<p class=\"note\">")
531557 if templ_7745c5c3_Err != nil {
532558 return templ_7745c5c3_Err
533559 }
534- var templ_7745c5c3_Var23 string
535- templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)
560+ var templ_7745c5c3_Var25 string
561+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)
536562 if templ_7745c5c3_Err != nil {
537- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 129, Col: 27}
563+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 130, Col: 27}
538564 }
539- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))
565+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
540566 if templ_7745c5c3_Err != nil {
541567 return templ_7745c5c3_Err
542568 }
543- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</p>")
569+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</p>")
544570 if templ_7745c5c3_Err != nil {
545571 return templ_7745c5c3_Err
546572 }
547573 }
548574 return nil
549575 })
550- templ_7745c5c3_Err = Shell(l, refsRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer)
576+ templ_7745c5c3_Err = Shell(l, refsRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer)
551577 if templ_7745c5c3_Err != nil {
552578 return templ_7745c5c3_Err
553579 }
@@ -571,112 +597,112 @@ func refRow(kind string, r RefRow) templ.Component {
571597 }()
572598 }
573599 ctx = templ.InitializeContext(ctx)
574- templ_7745c5c3_Var24 := templ.GetChildren(ctx)
575- if templ_7745c5c3_Var24 == nil {
576- templ_7745c5c3_Var24 = templ.NopComponent
600+ templ_7745c5c3_Var26 := templ.GetChildren(ctx)
601+ if templ_7745c5c3_Var26 == nil {
602+ templ_7745c5c3_Var26 = templ.NopComponent
577603 }
578604 ctx = templ.ClearChildren(ctx)
579- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<tr")
605+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<tr")
580606 if templ_7745c5c3_Err != nil {
581607 return templ_7745c5c3_Err
582608 }
583609 if r.Cursor {
584- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " data-cursor")
610+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, " data-cursor")
585611 if templ_7745c5c3_Err != nil {
586612 return templ_7745c5c3_Err
587613 }
588614 }
589- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-kind mute\">")
615+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-kind mute\">")
590616 if templ_7745c5c3_Err != nil {
591617 return templ_7745c5c3_Err
592618 }
593- var templ_7745c5c3_Var25 string
594- templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(kind)
619+ var templ_7745c5c3_Var27 string
620+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(kind)
595621 if templ_7745c5c3_Err != nil {
596- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 137, Col: 32}
622+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 138, Col: 32}
597623 }
598- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
624+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
599625 if templ_7745c5c3_Err != nil {
600626 return templ_7745c5c3_Err
601627 }
602- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</td><td class=\"c-name\"><a href=\"")
628+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</td><td class=\"c-name\"><a href=\"")
603629 if templ_7745c5c3_Err != nil {
604630 return templ_7745c5c3_Err
605631 }
606- var templ_7745c5c3_Var26 templ.SafeURL
607- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
632+ var templ_7745c5c3_Var28 templ.SafeURL
633+ templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
608634 if templ_7745c5c3_Err != nil {
609- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 139, Col: 34}
635+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 140, Col: 34}
610636 }
611- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
637+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
612638 if templ_7745c5c3_Err != nil {
613639 return templ_7745c5c3_Err
614640 }
615- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\">")
641+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\">")
616642 if templ_7745c5c3_Err != nil {
617643 return templ_7745c5c3_Err
618644 }
619- var templ_7745c5c3_Var27 string
620- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
645+ var templ_7745c5c3_Var29 string
646+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
621647 if templ_7745c5c3_Err != nil {
622- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 139, Col: 45}
648+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 140, Col: 45}
623649 }
624- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
650+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
625651 if templ_7745c5c3_Err != nil {
626652 return templ_7745c5c3_Err
627653 }
628- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</a> ")
654+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</a> ")
629655 if templ_7745c5c3_Err != nil {
630656 return templ_7745c5c3_Err
631657 }
632658 if r.Default {
633- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<span class=\"pill\">default</span>")
659+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<span class=\"pill\">default</span>")
634660 if templ_7745c5c3_Err != nil {
635661 return templ_7745c5c3_Err
636662 }
637663 }
638- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</td><td class=\"c-sha\">")
664+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</td><td class=\"c-sha\">")
639665 if templ_7745c5c3_Err != nil {
640666 return templ_7745c5c3_Err
641667 }
642668 if r.CHref != "" {
643- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<a href=\"")
669+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<a href=\"")
644670 if templ_7745c5c3_Err != nil {
645671 return templ_7745c5c3_Err
646672 }
647- var templ_7745c5c3_Var28 templ.SafeURL
648- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CHref))
673+ var templ_7745c5c3_Var30 templ.SafeURL
674+ templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CHref))
649675 if templ_7745c5c3_Err != nil {
650- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 146, Col: 36}
676+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 147, Col: 36}
651677 }
652- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
678+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
653679 if templ_7745c5c3_Err != nil {
654680 return templ_7745c5c3_Err
655681 }
656- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "\">")
682+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">")
657683 if templ_7745c5c3_Err != nil {
658684 return templ_7745c5c3_Err
659685 }
660- var templ_7745c5c3_Var29 string
661- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)
686+ var templ_7745c5c3_Var31 string
687+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)
662688 if templ_7745c5c3_Err != nil {
663- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 146, Col: 48}
689+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 147, Col: 48}
664690 }
665- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
691+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
666692 if templ_7745c5c3_Err != nil {
667693 return templ_7745c5c3_Err
668694 }
669- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</a>")
695+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</a>")
670696 if templ_7745c5c3_Err != nil {
671697 return templ_7745c5c3_Err
672698 }
673699 } else {
674- var templ_7745c5c3_Var30 string
675- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)
700+ var templ_7745c5c3_Var32 string
701+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)
676702 if templ_7745c5c3_Err != nil {
677- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 148, Col: 13}
703+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 149, Col: 13}
678704 }
679- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
705+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
680706 if templ_7745c5c3_Err != nil {
681707 return templ_7745c5c3_Err
682708 }
@@ -685,69 +711,69 @@ func refRow(kind string, r RefRow) templ.Component {
685711 if templ_7745c5c3_Err != nil {
686712 return templ_7745c5c3_Err
687713 }
688- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</td><td class=\"c-sub\">")
714+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</td><td class=\"c-sub\">")
689715 if templ_7745c5c3_Err != nil {
690716 return templ_7745c5c3_Err
691717 }
692- var templ_7745c5c3_Var31 string
693- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(r.Subject)
718+ var templ_7745c5c3_Var33 string
719+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(r.Subject)
694720 if templ_7745c5c3_Err != nil {
695- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 152, Col: 31}
721+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 153, Col: 31}
696722 }
697- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
723+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
698724 if templ_7745c5c3_Err != nil {
699725 return templ_7745c5c3_Err
700726 }
701- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</td><td class=\"c-when dim\">")
727+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</td><td class=\"c-when dim\">")
702728 if templ_7745c5c3_Err != nil {
703729 return templ_7745c5c3_Err
704730 }
705731 if r.WhenAt != "" {
706- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<time datetime=\"")
732+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<time datetime=\"")
707733 if templ_7745c5c3_Err != nil {
708734 return templ_7745c5c3_Err
709735 }
710- var templ_7745c5c3_Var32 string
711- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
736+ var templ_7745c5c3_Var34 string
737+ templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
712738 if templ_7745c5c3_Err != nil {
713- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 155, Col: 29}
739+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 156, Col: 29}
714740 }
715- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
741+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
716742 if templ_7745c5c3_Err != nil {
717743 return templ_7745c5c3_Err
718744 }
719- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" title=\"")
745+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" title=\"")
720746 if templ_7745c5c3_Err != nil {
721747 return templ_7745c5c3_Err
722748 }
723- var templ_7745c5c3_Var33 string
724- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
749+ var templ_7745c5c3_Var35 string
750+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
725751 if templ_7745c5c3_Err != nil {
726- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 155, Col: 48}
752+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 156, Col: 48}
727753 }
728- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
754+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
729755 if templ_7745c5c3_Err != nil {
730756 return templ_7745c5c3_Err
731757 }
732- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\">")
758+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\">")
733759 if templ_7745c5c3_Err != nil {
734760 return templ_7745c5c3_Err
735761 }
736- var templ_7745c5c3_Var34 string
737- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(r.When)
762+ var templ_7745c5c3_Var36 string
763+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(r.When)
738764 if templ_7745c5c3_Err != nil {
739- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 155, Col: 59}
765+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 156, Col: 59}
740766 }
741- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
767+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
742768 if templ_7745c5c3_Err != nil {
743769 return templ_7745c5c3_Err
744770 }
745- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</time>")
771+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</time>")
746772 if templ_7745c5c3_Err != nil {
747773 return templ_7745c5c3_Err
748774 }
749775 }
750- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</td></tr>")
776+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</td></tr>")
751777 if templ_7745c5c3_Err != nil {
752778 return templ_7745c5c3_Err
753779 }
@@ -771,12 +797,12 @@ func refsRail(p RefsPage) templ.Component {
771797 }()
772798 }
773799 ctx = templ.InitializeContext(ctx)
774- templ_7745c5c3_Var35 := templ.GetChildren(ctx)
775- if templ_7745c5c3_Var35 == nil {
776- templ_7745c5c3_Var35 = templ.NopComponent
800+ templ_7745c5c3_Var37 := templ.GetChildren(ctx)
801+ if templ_7745c5c3_Var37 == nil {
802+ templ_7745c5c3_Var37 = templ.NopComponent
777803 }
778804 ctx = templ.ClearChildren(ctx)
779- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<section aria-labelledby=\"r-refs\"><h2 id=\"r-refs\">Overview</h2>")
805+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<section aria-labelledby=\"r-refs\"><h2 id=\"r-refs\">Overview</h2>")
780806 if templ_7745c5c3_Err != nil {
781807 return templ_7745c5c3_Err
782808 }
@@ -784,7 +810,7 @@ func refsRail(p RefsPage) templ.Component {
784810 if templ_7745c5c3_Err != nil {
785811 return templ_7745c5c3_Err
786812 }
787- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</section>")
813+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</section>")
788814 if templ_7745c5c3_Err != nil {
789815 return templ_7745c5c3_Err
790816 }
@@ -809,12 +835,12 @@ func State(l Layout, p StatePage) templ.Component {
809835 }()
810836 }
811837 ctx = templ.InitializeContext(ctx)
812- templ_7745c5c3_Var36 := templ.GetChildren(ctx)
813- if templ_7745c5c3_Var36 == nil {
814- templ_7745c5c3_Var36 = templ.NopComponent
838+ templ_7745c5c3_Var38 := templ.GetChildren(ctx)
839+ if templ_7745c5c3_Var38 == nil {
840+ templ_7745c5c3_Var38 = templ.NopComponent
815841 }
816842 ctx = templ.ClearChildren(ctx)
817- templ_7745c5c3_Var37 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
843+ templ_7745c5c3_Var39 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
818844 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
819845 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
820846 if !templ_7745c5c3_IsBuffer {
@@ -826,51 +852,51 @@ func State(l Layout, p StatePage) templ.Component {
826852 }()
827853 }
828854 ctx = templ.InitializeContext(ctx)
829- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<section class=\"state\" aria-labelledby=\"state-title\"><p class=\"eyebrow\">")
855+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<section class=\"state\" aria-labelledby=\"state-title\"><p class=\"eyebrow\">")
830856 if templ_7745c5c3_Err != nil {
831857 return templ_7745c5c3_Err
832858 }
833- var templ_7745c5c3_Var38 string
834- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(p.Code)
859+ var templ_7745c5c3_Var40 string
860+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(p.Code)
835861 if templ_7745c5c3_Err != nil {
836- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 172, Col: 30}
862+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 173, Col: 30}
837863 }
838- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))
864+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
839865 if templ_7745c5c3_Err != nil {
840866 return templ_7745c5c3_Err
841867 }
842- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</p><h1 id=\"state-title\">")
868+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</p><h1 id=\"state-title\">")
843869 if templ_7745c5c3_Err != nil {
844870 return templ_7745c5c3_Err
845871 }
846- var templ_7745c5c3_Var39 string
847- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
872+ var templ_7745c5c3_Var41 string
873+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
848874 if templ_7745c5c3_Err != nil {
849- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 173, Col: 33}
875+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 174, Col: 33}
850876 }
851- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))
877+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
852878 if templ_7745c5c3_Err != nil {
853879 return templ_7745c5c3_Err
854880 }
855- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</h1>")
881+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</h1>")
856882 if templ_7745c5c3_Err != nil {
857883 return templ_7745c5c3_Err
858884 }
859885 for _, line := range p.Lines {
860- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<p class=\"dim\">")
886+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<p class=\"dim\">")
861887 if templ_7745c5c3_Err != nil {
862888 return templ_7745c5c3_Err
863889 }
864- var templ_7745c5c3_Var40 string
865- templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(line)
890+ var templ_7745c5c3_Var42 string
891+ templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(line)
866892 if templ_7745c5c3_Err != nil {
867- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 175, Col: 25}
893+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 176, Col: 25}
868894 }
869- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
895+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
870896 if templ_7745c5c3_Err != nil {
871897 return templ_7745c5c3_Err
872898 }
873- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</p>")
899+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</p>")
874900 if templ_7745c5c3_Err != nil {
875901 return templ_7745c5c3_Err
876902 }
@@ -881,49 +907,49 @@ func State(l Layout, p StatePage) templ.Component {
881907 return templ_7745c5c3_Err
882908 }
883909 }
884- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<p class=\"state-back\">")
910+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<p class=\"state-back\">")
885911 if templ_7745c5c3_Err != nil {
886912 return templ_7745c5c3_Err
887913 }
888914 for _, b := range p.Back {
889- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<a href=\"")
915+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<a href=\"")
890916 if templ_7745c5c3_Err != nil {
891917 return templ_7745c5c3_Err
892918 }
893- var templ_7745c5c3_Var41 templ.SafeURL
894- templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(b.Href))
919+ var templ_7745c5c3_Var43 templ.SafeURL
920+ templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(b.Href))
895921 if templ_7745c5c3_Err != nil {
896- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 182, Col: 36}
922+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 183, Col: 36}
897923 }
898- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
924+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
899925 if templ_7745c5c3_Err != nil {
900926 return templ_7745c5c3_Err
901927 }
902- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\">")
928+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\">")
903929 if templ_7745c5c3_Err != nil {
904930 return templ_7745c5c3_Err
905931 }
906- var templ_7745c5c3_Var42 string
907- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(b.Label)
932+ var templ_7745c5c3_Var44 string
933+ templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(b.Label)
908934 if templ_7745c5c3_Err != nil {
909- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 182, Col: 48}
935+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 183, Col: 48}
910936 }
911- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
937+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
912938 if templ_7745c5c3_Err != nil {
913939 return templ_7745c5c3_Err
914940 }
915- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</a>")
941+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</a>")
916942 if templ_7745c5c3_Err != nil {
917943 return templ_7745c5c3_Err
918944 }
919945 }
920- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</p></section>")
946+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</p></section>")
921947 if templ_7745c5c3_Err != nil {
922948 return templ_7745c5c3_Err
923949 }
924950 return nil
925951 })
926- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var37), templ_7745c5c3_Buffer)
952+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var39), templ_7745c5c3_Buffer)
927953 if templ_7745c5c3_Err != nil {
928954 return templ_7745c5c3_Err
929955 }
@@ -952,12 +978,12 @@ func Login(l Layout, p LoginPage) templ.Component {
952978 }()
953979 }
954980 ctx = templ.InitializeContext(ctx)
955- templ_7745c5c3_Var43 := templ.GetChildren(ctx)
956- if templ_7745c5c3_Var43 == nil {
957- templ_7745c5c3_Var43 = templ.NopComponent
981+ templ_7745c5c3_Var45 := templ.GetChildren(ctx)
982+ if templ_7745c5c3_Var45 == nil {
983+ templ_7745c5c3_Var45 = templ.NopComponent
958984 }
959985 ctx = templ.ClearChildren(ctx)
960- templ_7745c5c3_Var44 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
986+ templ_7745c5c3_Var46 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
961987 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
962988 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
963989 if !templ_7745c5c3_IsBuffer {
@@ -969,75 +995,75 @@ func Login(l Layout, p LoginPage) templ.Component {
969995 }()
970996 }
971997 ctx = templ.InitializeContext(ctx)
972- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")
998+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")
973999 if templ_7745c5c3_Err != nil {
9741000 return templ_7745c5c3_Err
9751001 }
976- var templ_7745c5c3_Var45 string
977- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
1002+ var templ_7745c5c3_Var47 string
1003+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
9781004 if templ_7745c5c3_Err != nil {
979- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 197, Col: 33}
1005+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 198, Col: 33}
9801006 }
981- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))
1007+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
9821008 if templ_7745c5c3_Err != nil {
9831009 return templ_7745c5c3_Err
9841010 }
985- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</h1>")
1011+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</h1>")
9861012 if templ_7745c5c3_Err != nil {
9871013 return templ_7745c5c3_Err
9881014 }
9891015 for _, line := range p.Lines {
990- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<p class=\"dim\">")
1016+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<p class=\"dim\">")
9911017 if templ_7745c5c3_Err != nil {
9921018 return templ_7745c5c3_Err
9931019 }
994- var templ_7745c5c3_Var46 string
995- templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(line)
1020+ var templ_7745c5c3_Var48 string
1021+ templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(line)
9961022 if templ_7745c5c3_Err != nil {
997- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 199, Col: 25}
1023+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 200, Col: 25}
9981024 }
999- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))
1025+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
10001026 if templ_7745c5c3_Err != nil {
10011027 return templ_7745c5c3_Err
10021028 }
1003- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</p>")
1029+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</p>")
10041030 if templ_7745c5c3_Err != nil {
10051031 return templ_7745c5c3_Err
10061032 }
10071033 }
1008- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<form class=\"state-act\" method=\"post\" action=\"")
1034+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<form class=\"state-act\" method=\"post\" action=\"")
10091035 if templ_7745c5c3_Err != nil {
10101036 return templ_7745c5c3_Err
10111037 }
1012- var templ_7745c5c3_Var47 templ.SafeURL
1013- templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Action))
1038+ var templ_7745c5c3_Var49 templ.SafeURL
1039+ templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Action))
10141040 if templ_7745c5c3_Err != nil {
1015- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 201, Col: 73}
1041+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 202, Col: 73}
10161042 }
1017- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
1043+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
10181044 if templ_7745c5c3_Err != nil {
10191045 return templ_7745c5c3_Err
10201046 }
1021- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\" data-autosubmit><button type=\"submit\" class=\"btn primary\">")
1047+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "\" data-autosubmit><button type=\"submit\" class=\"btn primary\">")
10221048 if templ_7745c5c3_Err != nil {
10231049 return templ_7745c5c3_Err
10241050 }
1025- var templ_7745c5c3_Var48 string
1026- templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)
1051+ var templ_7745c5c3_Var50 string
1052+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)
10271053 if templ_7745c5c3_Err != nil {
1028- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 202, Col: 56}
1054+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 203, Col: 56}
10291055 }
1030- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
1056+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
10311057 if templ_7745c5c3_Err != nil {
10321058 return templ_7745c5c3_Err
10331059 }
1034- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</button></form><p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")
1060+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</button></form><p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")
10351061 if templ_7745c5c3_Err != nil {
10361062 return templ_7745c5c3_Err
10371063 }
10381064 return nil
10391065 })
1040- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var44), templ_7745c5c3_Buffer)
1066+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var46), templ_7745c5c3_Buffer)
10411067 if templ_7745c5c3_Err != nil {
10421068 return templ_7745c5c3_Err
10431069 }
@@ -1063,12 +1089,12 @@ func PasskeyLogin(l Layout, p PasskeyLoginPage) templ.Component {
10631089 }()
10641090 }
10651091 ctx = templ.InitializeContext(ctx)
1066- templ_7745c5c3_Var49 := templ.GetChildren(ctx)
1067- if templ_7745c5c3_Var49 == nil {
1068- templ_7745c5c3_Var49 = templ.NopComponent
1092+ templ_7745c5c3_Var51 := templ.GetChildren(ctx)
1093+ if templ_7745c5c3_Var51 == nil {
1094+ templ_7745c5c3_Var51 = templ.NopComponent
10691095 }
10701096 ctx = templ.ClearChildren(ctx)
1071- templ_7745c5c3_Var50 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1097+ templ_7745c5c3_Var52 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
10721098 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
10731099 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
10741100 if !templ_7745c5c3_IsBuffer {
@@ -1080,88 +1106,88 @@ func PasskeyLogin(l Layout, p PasskeyLoginPage) templ.Component {
10801106 }()
10811107 }
10821108 ctx = templ.InitializeContext(ctx)
1083- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")
1109+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")
10841110 if templ_7745c5c3_Err != nil {
10851111 return templ_7745c5c3_Err
10861112 }
1087- var templ_7745c5c3_Var51 string
1088- templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
1113+ var templ_7745c5c3_Var53 string
1114+ templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
10891115 if templ_7745c5c3_Err != nil {
1090- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 214, Col: 33}
1116+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 215, Col: 33}
10911117 }
1092- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))
1118+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53))
10931119 if templ_7745c5c3_Err != nil {
10941120 return templ_7745c5c3_Err
10951121 }
1096- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</h1><form class=\"state-act\" data-js data-passkey-form><button type=\"submit\" class=\"btn primary\" data-passkey=\"login\" data-begin=\"")
1122+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</h1><form class=\"state-act\" data-js data-passkey-form><button type=\"submit\" class=\"btn primary\" data-passkey=\"login\" data-begin=\"")
10971123 if templ_7745c5c3_Err != nil {
10981124 return templ_7745c5c3_Err
10991125 }
1100- var templ_7745c5c3_Var52 string
1101- templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)
1126+ var templ_7745c5c3_Var54 string
1127+ templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)
11021128 if templ_7745c5c3_Err != nil {
1103- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 216, Col: 87}
1129+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 217, Col: 87}
11041130 }
1105- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var52)
1131+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var54)
11061132 if templ_7745c5c3_Err != nil {
11071133 return templ_7745c5c3_Err
11081134 }
1109- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\" data-finish=\"")
1135+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\" data-finish=\"")
11101136 if templ_7745c5c3_Err != nil {
11111137 return templ_7745c5c3_Err
11121138 }
1113- var templ_7745c5c3_Var53 string
1114- templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)
1139+ var templ_7745c5c3_Var55 string
1140+ templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)
11151141 if templ_7745c5c3_Err != nil {
1116- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 216, Col: 112}
1142+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 217, Col: 112}
11171143 }
1118- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var53)
1144+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var55)
11191145 if templ_7745c5c3_Err != nil {
11201146 return templ_7745c5c3_Err
11211147 }
1122- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "\">")
1148+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "\">")
11231149 if templ_7745c5c3_Err != nil {
11241150 return templ_7745c5c3_Err
11251151 }
1126- var templ_7745c5c3_Var54 string
1127- templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)
1152+ var templ_7745c5c3_Var56 string
1153+ templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)
11281154 if templ_7745c5c3_Err != nil {
1129- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 216, Col: 125}
1155+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 217, Col: 125}
11301156 }
1131- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))
1157+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
11321158 if templ_7745c5c3_Err != nil {
11331159 return templ_7745c5c3_Err
11341160 }
1135- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</button></form><p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")
1161+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</button></form><p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")
11361162 if templ_7745c5c3_Err != nil {
11371163 return templ_7745c5c3_Err
11381164 }
11391165 for _, line := range p.Lines {
1140- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<p class=\"dim\">")
1166+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<p class=\"dim\">")
11411167 if templ_7745c5c3_Err != nil {
11421168 return templ_7745c5c3_Err
11431169 }
1144- var templ_7745c5c3_Var55 string
1145- templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(line)
1170+ var templ_7745c5c3_Var57 string
1171+ templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(line)
11461172 if templ_7745c5c3_Err != nil {
1147- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 220, Col: 25}
1173+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 221, Col: 25}
11481174 }
1149- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))
1175+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
11501176 if templ_7745c5c3_Err != nil {
11511177 return templ_7745c5c3_Err
11521178 }
1153- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</p>")
1179+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</p>")
11541180 if templ_7745c5c3_Err != nil {
11551181 return templ_7745c5c3_Err
11561182 }
11571183 }
1158- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")
1184+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")
11591185 if templ_7745c5c3_Err != nil {
11601186 return templ_7745c5c3_Err
11611187 }
11621188 return nil
11631189 })
1164- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var50), templ_7745c5c3_Buffer)
1190+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var52), templ_7745c5c3_Buffer)
11651191 if templ_7745c5c3_Err != nil {
11661192 return templ_7745c5c3_Err
11671193 }
@@ -1186,12 +1212,12 @@ func Passkeys(l Layout, p PasskeysPage) templ.Component {
11861212 }()
11871213 }
11881214 ctx = templ.InitializeContext(ctx)
1189- templ_7745c5c3_Var56 := templ.GetChildren(ctx)
1190- if templ_7745c5c3_Var56 == nil {
1191- templ_7745c5c3_Var56 = templ.NopComponent
1215+ templ_7745c5c3_Var58 := templ.GetChildren(ctx)
1216+ if templ_7745c5c3_Var58 == nil {
1217+ templ_7745c5c3_Var58 = templ.NopComponent
11921218 }
11931219 ctx = templ.ClearChildren(ctx)
1194- templ_7745c5c3_Var57 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1220+ templ_7745c5c3_Var59 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
11951221 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
11961222 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
11971223 if !templ_7745c5c3_IsBuffer {
@@ -1203,231 +1229,231 @@ func Passkeys(l Layout, p PasskeysPage) templ.Component {
12031229 }()
12041230 }
12051231 ctx = templ.InitializeContext(ctx)
1206- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<h2 class=\"rule-h\"><span>Passkeys</span><span class=\"count\">")
1232+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<h2 class=\"rule-h\"><span>Passkeys</span><span class=\"count\">")
12071233 if templ_7745c5c3_Err != nil {
12081234 return templ_7745c5c3_Err
12091235 }
1210- var templ_7745c5c3_Var58 string
1211- templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Rows)))
1236+ var templ_7745c5c3_Var60 string
1237+ templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Rows)))
12121238 if templ_7745c5c3_Err != nil {
1213- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 230, Col: 81}
1239+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 231, Col: 81}
12141240 }
1215- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58))
1241+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var60))
12161242 if templ_7745c5c3_Err != nil {
12171243 return templ_7745c5c3_Err
12181244 }
1219- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</span></h2><p class=\"dim\">A passkey signs you in from this device or your phone, without a terminal. Removing one signs every browser out.</p>")
1245+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</span></h2><p class=\"dim\">A passkey signs you in from this device or your phone, without a terminal. Removing one signs every browser out.</p>")
12201246 if templ_7745c5c3_Err != nil {
12211247 return templ_7745c5c3_Err
12221248 }
12231249 if len(p.Rows) > 0 {
1224- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<table class=\"list passkeys\" aria-label=\"Passkeys\"><thead><tr><th class=\"cur\"></th><th class=\"c-name\">Name</th><th class=\"c-when\">Created</th><th class=\"c-when\">Last used</th><th class=\"c-sync\">Synced</th><th class=\"c-act\"><span class=\"sr\">Remove</span></th></tr></thead> <tbody>")
1250+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<table class=\"list passkeys\" aria-label=\"Passkeys\"><thead><tr><th class=\"cur\"></th><th class=\"c-name\">Name</th><th class=\"c-when\">Created</th><th class=\"c-when\">Last used</th><th class=\"c-sync\">Synced</th><th class=\"c-act\"><span class=\"sr\">Remove</span></th></tr></thead> <tbody>")
12251251 if templ_7745c5c3_Err != nil {
12261252 return templ_7745c5c3_Err
12271253 }
12281254 for _, r := range p.Rows {
1229- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<tr><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-name\">")
1255+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "<tr><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-name\">")
12301256 if templ_7745c5c3_Err != nil {
12311257 return templ_7745c5c3_Err
12321258 }
1233- var templ_7745c5c3_Var59 string
1234- templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
1259+ var templ_7745c5c3_Var61 string
1260+ templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
12351261 if templ_7745c5c3_Err != nil {
1236- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 248, Col: 34}
1262+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 249, Col: 34}
12371263 }
1238- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59))
1264+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
12391265 if templ_7745c5c3_Err != nil {
12401266 return templ_7745c5c3_Err
12411267 }
1242- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</td><td class=\"c-when nowrap\"><time")
1268+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</td><td class=\"c-when nowrap\"><time")
12431269 if templ_7745c5c3_Err != nil {
12441270 return templ_7745c5c3_Err
12451271 }
12461272 if r.CreatedAt != "" {
1247- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, " datetime=\"")
1273+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, " datetime=\"")
12481274 if templ_7745c5c3_Err != nil {
12491275 return templ_7745c5c3_Err
12501276 }
1251- var templ_7745c5c3_Var60 string
1252- templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CreatedAt)
1277+ var templ_7745c5c3_Var62 string
1278+ templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CreatedAt)
12531279 if templ_7745c5c3_Err != nil {
1254- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 252, Col: 32}
1280+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 253, Col: 32}
12551281 }
1256- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)
1282+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var62)
12571283 if templ_7745c5c3_Err != nil {
12581284 return templ_7745c5c3_Err
12591285 }
1260- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "\"")
1286+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "\"")
12611287 if templ_7745c5c3_Err != nil {
12621288 return templ_7745c5c3_Err
12631289 }
12641290 }
1265- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, ">")
1291+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, ">")
12661292 if templ_7745c5c3_Err != nil {
12671293 return templ_7745c5c3_Err
12681294 }
1269- var templ_7745c5c3_Var61 string
1270- templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(r.Created)
1295+ var templ_7745c5c3_Var63 string
1296+ templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(r.Created)
12711297 if templ_7745c5c3_Err != nil {
1272- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 254, Col: 20}
1298+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 255, Col: 20}
12731299 }
1274- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
1300+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))
12751301 if templ_7745c5c3_Err != nil {
12761302 return templ_7745c5c3_Err
12771303 }
1278- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</time></td><td class=\"c-when nowrap\"><time")
1304+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "</time></td><td class=\"c-when nowrap\"><time")
12791305 if templ_7745c5c3_Err != nil {
12801306 return templ_7745c5c3_Err
12811307 }
12821308 if r.LastUsedAt != "" {
1283- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, " datetime=\"")
1309+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, " datetime=\"")
12841310 if templ_7745c5c3_Err != nil {
12851311 return templ_7745c5c3_Err
12861312 }
1287- var templ_7745c5c3_Var62 string
1288- templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.LastUsedAt)
1313+ var templ_7745c5c3_Var64 string
1314+ templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.LastUsedAt)
12891315 if templ_7745c5c3_Err != nil {
1290- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 259, Col: 33}
1316+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 260, Col: 33}
12911317 }
1292- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var62)
1318+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)
12931319 if templ_7745c5c3_Err != nil {
12941320 return templ_7745c5c3_Err
12951321 }
1296- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "\"")
1322+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "\"")
12971323 if templ_7745c5c3_Err != nil {
12981324 return templ_7745c5c3_Err
12991325 }
13001326 }
1301- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, ">")
1327+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, ">")
13021328 if templ_7745c5c3_Err != nil {
13031329 return templ_7745c5c3_Err
13041330 }
1305- var templ_7745c5c3_Var63 string
1306- templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(r.LastUsed)
1331+ var templ_7745c5c3_Var65 string
1332+ templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(r.LastUsed)
13071333 if templ_7745c5c3_Err != nil {
1308- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 261, Col: 21}
1334+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 262, Col: 21}
13091335 }
1310- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))
1336+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
13111337 if templ_7745c5c3_Err != nil {
13121338 return templ_7745c5c3_Err
13131339 }
1314- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "</time></td><td class=\"c-sync\">")
1340+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "</time></td><td class=\"c-sync\">")
13151341 if templ_7745c5c3_Err != nil {
13161342 return templ_7745c5c3_Err
13171343 }
13181344 if r.Synced {
1319- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "yes")
1345+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "yes")
13201346 if templ_7745c5c3_Err != nil {
13211347 return templ_7745c5c3_Err
13221348 }
13231349 } else {
1324- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "no")
1350+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "no")
13251351 if templ_7745c5c3_Err != nil {
13261352 return templ_7745c5c3_Err
13271353 }
13281354 }
1329- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "</td><td class=\"c-act\"><form method=\"post\" action=\"")
1355+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "</td><td class=\"c-act\"><form method=\"post\" action=\"")
13301356 if templ_7745c5c3_Err != nil {
13311357 return templ_7745c5c3_Err
13321358 }
1333- var templ_7745c5c3_Var64 templ.SafeURL
1334- templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Remove))
1359+ var templ_7745c5c3_Var66 templ.SafeURL
1360+ templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Remove))
13351361 if templ_7745c5c3_Err != nil {
1336- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 271, Col: 60}
1362+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 272, Col: 60}
13371363 }
1338- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64))
1364+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
13391365 if templ_7745c5c3_Err != nil {
13401366 return templ_7745c5c3_Err
13411367 }
1342- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "\"><button type=\"submit\" class=\"btn danger\" aria-label=\"")
1368+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "\"><button type=\"submit\" class=\"btn danger\" aria-label=\"")
13431369 if templ_7745c5c3_Err != nil {
13441370 return templ_7745c5c3_Err
13451371 }
1346- var templ_7745c5c3_Var65 string
1347- templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.ResolveAttributeValue("Remove passkey " + r.Name)
1372+ var templ_7745c5c3_Var67 string
1373+ templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.ResolveAttributeValue("Remove passkey " + r.Name)
13481374 if templ_7745c5c3_Err != nil {
1349- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 272, Col: 89}
1375+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 273, Col: 89}
13501376 }
1351- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var65)
1377+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var67)
13521378 if templ_7745c5c3_Err != nil {
13531379 return templ_7745c5c3_Err
13541380 }
1355- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "\">Remove</button></form></td></tr>")
1381+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "\">Remove</button></form></td></tr>")
13561382 if templ_7745c5c3_Err != nil {
13571383 return templ_7745c5c3_Err
13581384 }
13591385 }
1360- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "</tbody></table>")
1386+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "</tbody></table>")
13611387 if templ_7745c5c3_Err != nil {
13621388 return templ_7745c5c3_Err
13631389 }
13641390 } else {
1365- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "<p class=\"lede dim\">no passkeys yet.</p>")
1391+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "<p class=\"lede dim\">no passkeys yet.</p>")
13661392 if templ_7745c5c3_Err != nil {
13671393 return templ_7745c5c3_Err
13681394 }
13691395 }
1370- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, " ")
1396+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, " ")
13711397 if templ_7745c5c3_Err != nil {
13721398 return templ_7745c5c3_Err
13731399 }
13741400 if p.Full {
1375- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "<p class=\"dim\">")
1401+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "<p class=\"dim\">")
13761402 if templ_7745c5c3_Err != nil {
13771403 return templ_7745c5c3_Err
13781404 }
1379- var templ_7745c5c3_Var66 string
1380- templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Max))
1405+ var templ_7745c5c3_Var68 string
1406+ templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Max))
13811407 if templ_7745c5c3_Err != nil {
1382- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 283, Col: 31}
1408+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 284, Col: 31}
13831409 }
1384- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
1410+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
13851411 if templ_7745c5c3_Err != nil {
13861412 return templ_7745c5c3_Err
13871413 }
1388- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, " passkeys are registered, the most there can be. Remove one to add another.</p>")
1414+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, " passkeys are registered, the most there can be. Remove one to add another.</p>")
13891415 if templ_7745c5c3_Err != nil {
13901416 return templ_7745c5c3_Err
13911417 }
13921418 } else {
1393- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "<form class=\"passkey-add\" data-js data-passkey-form><label for=\"passkey-name\" class=\"sr\">Name of the new passkey</label> <input id=\"passkey-name\" type=\"text\" placeholder=\"Name, for example Pixel 9\" autocomplete=\"off\" data-passkey-name> <button type=\"submit\" class=\"btn primary\" data-passkey=\"register\" data-begin=\"")
1419+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "<form class=\"passkey-add\" data-js data-passkey-form><label for=\"passkey-name\" class=\"sr\">Name of the new passkey</label> <input id=\"passkey-name\" type=\"text\" placeholder=\"Name, for example Pixel 9\" autocomplete=\"off\" data-passkey-name> <button type=\"submit\" class=\"btn primary\" data-passkey=\"register\" data-begin=\"")
13941420 if templ_7745c5c3_Err != nil {
13951421 return templ_7745c5c3_Err
13961422 }
1397- var templ_7745c5c3_Var67 string
1398- templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)
1423+ var templ_7745c5c3_Var69 string
1424+ templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)
13991425 if templ_7745c5c3_Err != nil {
1400- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 288, Col: 90}
1426+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 289, Col: 90}
14011427 }
1402- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var67)
1428+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var69)
14031429 if templ_7745c5c3_Err != nil {
14041430 return templ_7745c5c3_Err
14051431 }
1406- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "\" data-finish=\"")
1432+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, "\" data-finish=\"")
14071433 if templ_7745c5c3_Err != nil {
14081434 return templ_7745c5c3_Err
14091435 }
1410- var templ_7745c5c3_Var68 string
1411- templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)
1436+ var templ_7745c5c3_Var70 string
1437+ templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)
14121438 if templ_7745c5c3_Err != nil {
1413- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 288, Col: 115}
1439+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 289, Col: 115}
14141440 }
1415- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var68)
1441+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var70)
14161442 if templ_7745c5c3_Err != nil {
14171443 return templ_7745c5c3_Err
14181444 }
1419- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "\">Add a passkey</button></form>")
1445+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, "\">Add a passkey</button></form>")
14201446 if templ_7745c5c3_Err != nil {
14211447 return templ_7745c5c3_Err
14221448 }
14231449 }
1424- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, " <p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")
1450+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, " <p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")
14251451 if templ_7745c5c3_Err != nil {
14261452 return templ_7745c5c3_Err
14271453 }
14281454 return nil
14291455 })
1430- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var57), templ_7745c5c3_Buffer)
1456+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var59), templ_7745c5c3_Buffer)
14311457 if templ_7745c5c3_Err != nil {
14321458 return templ_7745c5c3_Err
14331459 }
@@ -266,14 +266,14 @@ func latestTagFact(t LatestTag) templ.Component {
266 if templ_7745c5c3_Err != nil {266 if templ_7745c5c3_Err != nil {
267 return templ_7745c5c3_Err267 return templ_7745c5c3_Err
268 }268 }
269- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\"><span class=\"ico i-tag\" aria-hidden=\"true\"></span>")269+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "\">")
270 if templ_7745c5c3_Err != nil {270 if templ_7745c5c3_Err != nil {
271 return templ_7745c5c3_Err271 return templ_7745c5c3_Err
272 }272 }
273 var templ_7745c5c3_Var12 string273 var templ_7745c5c3_Var12 string
274 templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(t.Name)274 templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(t.Name)
275 if templ_7745c5c3_Err != nil {275 if templ_7745c5c3_Err != nil {
276- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 71, Col: 132}276+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 71, Col: 82}
277 }277 }
278 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))278 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
279 if templ_7745c5c3_Err != nil {279 if templ_7745c5c3_Err != nil {
@@ -284,78 +284,104 @@ func latestTagFact(t LatestTag) templ.Component {
284 return templ_7745c5c3_Err284 return templ_7745c5c3_Err
285 }285 }
286 if t.WhenAt != "" {286 if t.WhenAt != "" {
287- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<time class=\"dim\" datetime=\"")
288- if templ_7745c5c3_Err != nil {
289- return templ_7745c5c3_Err
290- }
291 var templ_7745c5c3_Var13 string287 var templ_7745c5c3_Var13 string
292- templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)288+ templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
293 if templ_7745c5c3_Err != nil {289 if templ_7745c5c3_Err != nil {
294- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 41}290+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 12}
295 }291 }
296- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)292+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
297 if templ_7745c5c3_Err != nil {293 if templ_7745c5c3_Err != nil {
298 return templ_7745c5c3_Err294 return templ_7745c5c3_Err
299 }295 }
300- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" title=\"")296+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<time datetime=\"")
301 if templ_7745c5c3_Err != nil {297 if templ_7745c5c3_Err != nil {
302 return templ_7745c5c3_Err298 return templ_7745c5c3_Err
303 }299 }
304 var templ_7745c5c3_Var14 string300 var templ_7745c5c3_Var14 string
305 templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)301 templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)
306 if templ_7745c5c3_Err != nil {302 if templ_7745c5c3_Err != nil {
307- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 60}303+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 39}
308 }304 }
309 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)305 _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
310 if templ_7745c5c3_Err != nil {306 if templ_7745c5c3_Err != nil {
311 return templ_7745c5c3_Err307 return templ_7745c5c3_Err
312 }308 }
313- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")309+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\" title=\"")
314 if templ_7745c5c3_Err != nil {310 if templ_7745c5c3_Err != nil {
315 return templ_7745c5c3_Err311 return templ_7745c5c3_Err
316 }312 }
317 var templ_7745c5c3_Var15 string313 var templ_7745c5c3_Var15 string
318- templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(t.When)314+ templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.ResolveAttributeValue(t.WhenAt)
315+ if templ_7745c5c3_Err != nil {
316+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 58}
317+ }
318+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
319 if templ_7745c5c3_Err != nil {319 if templ_7745c5c3_Err != nil {
320- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 71}320+ return templ_7745c5c3_Err
321+ }
322+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\">")
323+ if templ_7745c5c3_Err != nil {
324+ return templ_7745c5c3_Err
325+ }
326+ var templ_7745c5c3_Var16 string
327+ templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(t.When)
328+ if templ_7745c5c3_Err != nil {
329+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 73, Col: 69}
321 }330 }
322- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))331+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
323 if templ_7745c5c3_Err != nil {332 if templ_7745c5c3_Err != nil {
324 return templ_7745c5c3_Err333 return templ_7745c5c3_Err
325 }334 }
326- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</time> ")335+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "</time>")
327 if templ_7745c5c3_Err != nil {336 if templ_7745c5c3_Err != nil {
328 return templ_7745c5c3_Err337 return templ_7745c5c3_Err
329 }338 }
330 }339 }
340+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, " ")
341+ if templ_7745c5c3_Err != nil {
342+ return templ_7745c5c3_Err
343+ }
344+ var templ_7745c5c3_Var17 string
345+ templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(" · ")
346+ if templ_7745c5c3_Err != nil {
347+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 75, Col: 11}
348+ }
349+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
350+ if templ_7745c5c3_Err != nil {
351+ return templ_7745c5c3_Err
352+ }
353+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " ")
354+ if templ_7745c5c3_Err != nil {
355+ return templ_7745c5c3_Err
356+ }
331 }357 }
332- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "<a class=\"all-tags\" href=\"")358+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<a class=\"all-tags\" href=\"")
333 if templ_7745c5c3_Err != nil {359 if templ_7745c5c3_Err != nil {
334 return templ_7745c5c3_Err360 return templ_7745c5c3_Err
335 }361 }
336- var templ_7745c5c3_Var16 templ.SafeURL362+ var templ_7745c5c3_Var18 templ.SafeURL
337- templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(t.RefsHref))363+ templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(t.RefsHref))
338 if templ_7745c5c3_Err != nil {364 if templ_7745c5c3_Err != nil {
339- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 76, Col: 54}365+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 77, Col: 54}
340 }366 }
341- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))367+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
342 if templ_7745c5c3_Err != nil {368 if templ_7745c5c3_Err != nil {
343 return templ_7745c5c3_Err369 return templ_7745c5c3_Err
344 }370 }
345- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "\">")371+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\">")
346 if templ_7745c5c3_Err != nil {372 if templ_7745c5c3_Err != nil {
347 return templ_7745c5c3_Err373 return templ_7745c5c3_Err
348 }374 }
349- var templ_7745c5c3_Var17 string375+ var templ_7745c5c3_Var19 string
350- templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(plural(t.Count, "tag", "tags"))376+ templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(plural(t.Count, "tag", "tags"))
351 if templ_7745c5c3_Err != nil {377 if templ_7745c5c3_Err != nil {
352- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 76, Col: 89}378+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 77, Col: 89}
353 }379 }
354- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))380+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
355 if templ_7745c5c3_Err != nil {381 if templ_7745c5c3_Err != nil {
356 return templ_7745c5c3_Err382 return templ_7745c5c3_Err
357 }383 }
358- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "</a></dd>")384+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</a></dd>")
359 if templ_7745c5c3_Err != nil {385 if templ_7745c5c3_Err != nil {
360 return templ_7745c5c3_Err386 return templ_7745c5c3_Err
361 }387 }
@@ -379,12 +405,12 @@ func summaryRail(p SummaryPage) templ.Component {
379 }()405 }()
380 }406 }
381 ctx = templ.InitializeContext(ctx)407 ctx = templ.InitializeContext(ctx)
382- templ_7745c5c3_Var18 := templ.GetChildren(ctx)408+ templ_7745c5c3_Var20 := templ.GetChildren(ctx)
383- if templ_7745c5c3_Var18 == nil {409+ if templ_7745c5c3_Var20 == nil {
384- templ_7745c5c3_Var18 = templ.NopComponent410+ templ_7745c5c3_Var20 = templ.NopComponent
385 }411 }
386 ctx = templ.ClearChildren(ctx)412 ctx = templ.ClearChildren(ctx)
387- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "<section aria-labelledby=\"r-clone\"><h2 id=\"r-clone\">Clone</h2>")413+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<section aria-labelledby=\"r-clone\"><h2 id=\"r-clone\">Clone</h2>")
388 if templ_7745c5c3_Err != nil {414 if templ_7745c5c3_Err != nil {
389 return templ_7745c5c3_Err415 return templ_7745c5c3_Err
390 }416 }
@@ -392,25 +418,25 @@ func summaryRail(p SummaryPage) templ.Component {
392 if templ_7745c5c3_Err != nil {418 if templ_7745c5c3_Err != nil {
393 return templ_7745c5c3_Err419 return templ_7745c5c3_Err
394 }420 }
395- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "</section><section aria-labelledby=\"r-facts\"><h2 id=\"r-facts\">About</h2><dl class=\"kv\">")421+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</section><section aria-labelledby=\"r-facts\"><h2 id=\"r-facts\">About</h2><dl class=\"kv\">")
396 if templ_7745c5c3_Err != nil {422 if templ_7745c5c3_Err != nil {
397 return templ_7745c5c3_Err423 return templ_7745c5c3_Err
398 }424 }
399 for _, f := range p.Facts {425 for _, f := range p.Facts {
400- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "<dt>")426+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<dt>")
401 if templ_7745c5c3_Err != nil {427 if templ_7745c5c3_Err != nil {
402 return templ_7745c5c3_Err428 return templ_7745c5c3_Err
403 }429 }
404- var templ_7745c5c3_Var19 string430+ var templ_7745c5c3_Var21 string
405- templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(f.Key)431+ templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(f.Key)
406 if templ_7745c5c3_Err != nil {432 if templ_7745c5c3_Err != nil {
407- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 87, Col: 15}433+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 88, Col: 15}
408 }434 }
409- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))435+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
410 if templ_7745c5c3_Err != nil {436 if templ_7745c5c3_Err != nil {
411 return templ_7745c5c3_Err437 return templ_7745c5c3_Err
412 }438 }
413- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</dt>")439+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "</dt>")
414 if templ_7745c5c3_Err != nil {440 if templ_7745c5c3_Err != nil {
415 return templ_7745c5c3_Err441 return templ_7745c5c3_Err
416 }442 }
@@ -425,25 +451,25 @@ func summaryRail(p SummaryPage) templ.Component {
425 return templ_7745c5c3_Err451 return templ_7745c5c3_Err
426 }452 }
427 }453 }
428- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "</dl></section>")454+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "</dl></section>")
429 if templ_7745c5c3_Err != nil {455 if templ_7745c5c3_Err != nil {
430 return templ_7745c5c3_Err456 return templ_7745c5c3_Err
431 }457 }
432 if len(p.Recent) > 0 {458 if len(p.Recent) > 0 {
433- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, "<section aria-labelledby=\"r-recent\"><h2 id=\"r-recent\">Recent commits <a href=\"")459+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "<section aria-labelledby=\"r-recent\"><h2 id=\"r-recent\">Recent commits <a href=\"")
434 if templ_7745c5c3_Err != nil {460 if templ_7745c5c3_Err != nil {
435 return templ_7745c5c3_Err461 return templ_7745c5c3_Err
436 }462 }
437- var templ_7745c5c3_Var20 templ.SafeURL463+ var templ_7745c5c3_Var22 templ.SafeURL
438- templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.AllHref))464+ templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.AllHref))
439 if templ_7745c5c3_Err != nil {465 if templ_7745c5c3_Err != nil {
440- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 96, Col: 105}466+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 97, Col: 105}
441 }467 }
442- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))468+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
443 if templ_7745c5c3_Err != nil {469 if templ_7745c5c3_Err != nil {
444 return templ_7745c5c3_Err470 return templ_7745c5c3_Err
445 }471 }
446- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "\">View all</a></h2>")472+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "\">View all</a></h2>")
447 if templ_7745c5c3_Err != nil {473 if templ_7745c5c3_Err != nil {
448 return templ_7745c5c3_Err474 return templ_7745c5c3_Err
449 }475 }
@@ -451,7 +477,7 @@ func summaryRail(p SummaryPage) templ.Component {
451 if templ_7745c5c3_Err != nil {477 if templ_7745c5c3_Err != nil {
452 return templ_7745c5c3_Err478 return templ_7745c5c3_Err
453 }479 }
454- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "</section>")480+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</section>")
455 if templ_7745c5c3_Err != nil {481 if templ_7745c5c3_Err != nil {
456 return templ_7745c5c3_Err482 return templ_7745c5c3_Err
457 }483 }
@@ -463,7 +489,7 @@ func summaryRail(p SummaryPage) templ.Component {
463 }489 }
464 }490 }
465 if len(p.Outline) > 0 && p.Readme != nil {491 if len(p.Outline) > 0 && p.Readme != nil {
466- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "<section aria-labelledby=\"r-outline\"><h2 id=\"r-outline\">On this page</h2>")492+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<section aria-labelledby=\"r-outline\"><h2 id=\"r-outline\">On this page</h2>")
467 if templ_7745c5c3_Err != nil {493 if templ_7745c5c3_Err != nil {
468 return templ_7745c5c3_Err494 return templ_7745c5c3_Err
469 }495 }
@@ -471,7 +497,7 @@ func summaryRail(p SummaryPage) templ.Component {
471 if templ_7745c5c3_Err != nil {497 if templ_7745c5c3_Err != nil {
472 return templ_7745c5c3_Err498 return templ_7745c5c3_Err
473 }499 }
474- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "</section>")500+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</section>")
475 if templ_7745c5c3_Err != nil {501 if templ_7745c5c3_Err != nil {
476 return templ_7745c5c3_Err502 return templ_7745c5c3_Err
477 }503 }
@@ -497,12 +523,12 @@ func Refs(l Layout, p RefsPage) templ.Component {
497 }()523 }()
498 }524 }
499 ctx = templ.InitializeContext(ctx)525 ctx = templ.InitializeContext(ctx)
500- templ_7745c5c3_Var21 := templ.GetChildren(ctx)526+ templ_7745c5c3_Var23 := templ.GetChildren(ctx)
501- if templ_7745c5c3_Var21 == nil {527+ if templ_7745c5c3_Var23 == nil {
502- templ_7745c5c3_Var21 = templ.NopComponent528+ templ_7745c5c3_Var23 = templ.NopComponent
503 }529 }
504 ctx = templ.ClearChildren(ctx)530 ctx = templ.ClearChildren(ctx)
505- templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {531+ templ_7745c5c3_Var24 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
506 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context532 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
507 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)533 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
508 if !templ_7745c5c3_IsBuffer {534 if !templ_7745c5c3_IsBuffer {
@@ -514,7 +540,7 @@ func Refs(l Layout, p RefsPage) templ.Component {
514 }()540 }()
515 }541 }
516 ctx = templ.InitializeContext(ctx)542 ctx = templ.InitializeContext(ctx)
517- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "<div class=\"filter\"><label for=\"ref-filter\" class=\"sr\">filter</label> <input id=\"ref-filter\" type=\"search\" data-filter=\"#ref-rows > tr\" placeholder=\"Filter branches and tags…\" autocomplete=\"off\" hx-on:input=\"ermite.filter(this)\"></div><table class=\"list refs\" aria-label=\"Branches and tags\"><thead><tr><th class=\"cur\"></th><th class=\"c-kind\">kind</th><th class=\"c-name\">name</th><th class=\"c-sha\">commit</th><th class=\"c-sub\">subject</th><th class=\"c-when\">date</th></tr></thead>")543+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<div class=\"filter\"><label for=\"ref-filter\" class=\"sr\">filter</label> <input id=\"ref-filter\" type=\"search\" data-filter=\"#ref-rows > tr\" placeholder=\"Filter branches and tags…\" autocomplete=\"off\" hx-on:input=\"ermite.filter(this)\"></div><table class=\"list refs\" aria-label=\"Branches and tags\"><thead><tr><th class=\"cur\"></th><th class=\"c-kind\">kind</th><th class=\"c-name\">name</th><th class=\"c-sha\">commit</th><th class=\"c-sub\">subject</th><th class=\"c-when\">date</th></tr></thead>")
518 if templ_7745c5c3_Err != nil {544 if templ_7745c5c3_Err != nil {
519 return templ_7745c5c3_Err545 return templ_7745c5c3_Err
520 }546 }
@@ -522,32 +548,32 @@ func Refs(l Layout, p RefsPage) templ.Component {
522 if templ_7745c5c3_Err != nil {548 if templ_7745c5c3_Err != nil {
523 return templ_7745c5c3_Err549 return templ_7745c5c3_Err
524 }550 }
525- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "</table>")551+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</table>")
526 if templ_7745c5c3_Err != nil {552 if templ_7745c5c3_Err != nil {
527 return templ_7745c5c3_Err553 return templ_7745c5c3_Err
528 }554 }
529 if p.Note != "" {555 if p.Note != "" {
530- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "<p class=\"note\">")556+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<p class=\"note\">")
531 if templ_7745c5c3_Err != nil {557 if templ_7745c5c3_Err != nil {
532 return templ_7745c5c3_Err558 return templ_7745c5c3_Err
533 }559 }
534- var templ_7745c5c3_Var23 string560+ var templ_7745c5c3_Var25 string
535- templ_7745c5c3_Var23, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)561+ templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(p.Note)
536 if templ_7745c5c3_Err != nil {562 if templ_7745c5c3_Err != nil {
537- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 129, Col: 27}563+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 130, Col: 27}
538 }564 }
539- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var23))565+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))
540 if templ_7745c5c3_Err != nil {566 if templ_7745c5c3_Err != nil {
541 return templ_7745c5c3_Err567 return templ_7745c5c3_Err
542 }568 }
543- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "</p>")569+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "</p>")
544 if templ_7745c5c3_Err != nil {570 if templ_7745c5c3_Err != nil {
545 return templ_7745c5c3_Err571 return templ_7745c5c3_Err
546 }572 }
547 }573 }
548 return nil574 return nil
549 })575 })
550- templ_7745c5c3_Err = Shell(l, refsRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer)576+ templ_7745c5c3_Err = Shell(l, refsRail(p)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var24), templ_7745c5c3_Buffer)
551 if templ_7745c5c3_Err != nil {577 if templ_7745c5c3_Err != nil {
552 return templ_7745c5c3_Err578 return templ_7745c5c3_Err
553 }579 }
@@ -571,112 +597,112 @@ func refRow(kind string, r RefRow) templ.Component {
571 }()597 }()
572 }598 }
573 ctx = templ.InitializeContext(ctx)599 ctx = templ.InitializeContext(ctx)
574- templ_7745c5c3_Var24 := templ.GetChildren(ctx)600+ templ_7745c5c3_Var26 := templ.GetChildren(ctx)
575- if templ_7745c5c3_Var24 == nil {601+ if templ_7745c5c3_Var26 == nil {
576- templ_7745c5c3_Var24 = templ.NopComponent602+ templ_7745c5c3_Var26 = templ.NopComponent
577 }603 }
578 ctx = templ.ClearChildren(ctx)604 ctx = templ.ClearChildren(ctx)
579- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "<tr")605+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "<tr")
580 if templ_7745c5c3_Err != nil {606 if templ_7745c5c3_Err != nil {
581 return templ_7745c5c3_Err607 return templ_7745c5c3_Err
582 }608 }
583 if r.Cursor {609 if r.Cursor {
584- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, " data-cursor")610+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, " data-cursor")
585 if templ_7745c5c3_Err != nil {611 if templ_7745c5c3_Err != nil {
586 return templ_7745c5c3_Err612 return templ_7745c5c3_Err
587 }613 }
588 }614 }
589- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-kind mute\">")615+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-kind mute\">")
590 if templ_7745c5c3_Err != nil {616 if templ_7745c5c3_Err != nil {
591 return templ_7745c5c3_Err617 return templ_7745c5c3_Err
592 }618 }
593- var templ_7745c5c3_Var25 string619+ var templ_7745c5c3_Var27 string
594- templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(kind)620+ templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(kind)
595 if templ_7745c5c3_Err != nil {621 if templ_7745c5c3_Err != nil {
596- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 137, Col: 32}622+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 138, Col: 32}
597 }623 }
598- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25))624+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))
599 if templ_7745c5c3_Err != nil {625 if templ_7745c5c3_Err != nil {
600 return templ_7745c5c3_Err626 return templ_7745c5c3_Err
601 }627 }
602- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "</td><td class=\"c-name\"><a href=\"")628+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</td><td class=\"c-name\"><a href=\"")
603 if templ_7745c5c3_Err != nil {629 if templ_7745c5c3_Err != nil {
604 return templ_7745c5c3_Err630 return templ_7745c5c3_Err
605 }631 }
606- var templ_7745c5c3_Var26 templ.SafeURL632+ var templ_7745c5c3_Var28 templ.SafeURL
607- templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))633+ templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Href))
608 if templ_7745c5c3_Err != nil {634 if templ_7745c5c3_Err != nil {
609- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 139, Col: 34}635+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 140, Col: 34}
610 }636 }
611- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))637+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
612 if templ_7745c5c3_Err != nil {638 if templ_7745c5c3_Err != nil {
613 return templ_7745c5c3_Err639 return templ_7745c5c3_Err
614 }640 }
615- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "\">")641+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "\">")
616 if templ_7745c5c3_Err != nil {642 if templ_7745c5c3_Err != nil {
617 return templ_7745c5c3_Err643 return templ_7745c5c3_Err
618 }644 }
619- var templ_7745c5c3_Var27 string645+ var templ_7745c5c3_Var29 string
620- templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)646+ templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
621 if templ_7745c5c3_Err != nil {647 if templ_7745c5c3_Err != nil {
622- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 139, Col: 45}648+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 140, Col: 45}
623 }649 }
624- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27))650+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))
625 if templ_7745c5c3_Err != nil {651 if templ_7745c5c3_Err != nil {
626 return templ_7745c5c3_Err652 return templ_7745c5c3_Err
627 }653 }
628- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "</a> ")654+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</a> ")
629 if templ_7745c5c3_Err != nil {655 if templ_7745c5c3_Err != nil {
630 return templ_7745c5c3_Err656 return templ_7745c5c3_Err
631 }657 }
632 if r.Default {658 if r.Default {
633- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, "<span class=\"pill\">default</span>")659+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<span class=\"pill\">default</span>")
634 if templ_7745c5c3_Err != nil {660 if templ_7745c5c3_Err != nil {
635 return templ_7745c5c3_Err661 return templ_7745c5c3_Err
636 }662 }
637 }663 }
638- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 48, "</td><td class=\"c-sha\">")664+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "</td><td class=\"c-sha\">")
639 if templ_7745c5c3_Err != nil {665 if templ_7745c5c3_Err != nil {
640 return templ_7745c5c3_Err666 return templ_7745c5c3_Err
641 }667 }
642 if r.CHref != "" {668 if r.CHref != "" {
643- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 49, "<a href=\"")669+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "<a href=\"")
644 if templ_7745c5c3_Err != nil {670 if templ_7745c5c3_Err != nil {
645 return templ_7745c5c3_Err671 return templ_7745c5c3_Err
646 }672 }
647- var templ_7745c5c3_Var28 templ.SafeURL673+ var templ_7745c5c3_Var30 templ.SafeURL
648- templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CHref))674+ templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.CHref))
649 if templ_7745c5c3_Err != nil {675 if templ_7745c5c3_Err != nil {
650- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 146, Col: 36}676+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 147, Col: 36}
651 }677 }
652- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))678+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))
653 if templ_7745c5c3_Err != nil {679 if templ_7745c5c3_Err != nil {
654 return templ_7745c5c3_Err680 return templ_7745c5c3_Err
655 }681 }
656- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 50, "\">")682+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "\">")
657 if templ_7745c5c3_Err != nil {683 if templ_7745c5c3_Err != nil {
658 return templ_7745c5c3_Err684 return templ_7745c5c3_Err
659 }685 }
660- var templ_7745c5c3_Var29 string686+ var templ_7745c5c3_Var31 string
661- templ_7745c5c3_Var29, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)687+ templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)
662 if templ_7745c5c3_Err != nil {688 if templ_7745c5c3_Err != nil {
663- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 146, Col: 48}689+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 147, Col: 48}
664 }690 }
665- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var29))691+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
666 if templ_7745c5c3_Err != nil {692 if templ_7745c5c3_Err != nil {
667 return templ_7745c5c3_Err693 return templ_7745c5c3_Err
668 }694 }
669- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 51, "</a>")695+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</a>")
670 if templ_7745c5c3_Err != nil {696 if templ_7745c5c3_Err != nil {
671 return templ_7745c5c3_Err697 return templ_7745c5c3_Err
672 }698 }
673 } else {699 } else {
674- var templ_7745c5c3_Var30 string700+ var templ_7745c5c3_Var32 string
675- templ_7745c5c3_Var30, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)701+ templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(r.Short)
676 if templ_7745c5c3_Err != nil {702 if templ_7745c5c3_Err != nil {
677- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 148, Col: 13}703+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 149, Col: 13}
678 }704 }
679- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var30))705+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32))
680 if templ_7745c5c3_Err != nil {706 if templ_7745c5c3_Err != nil {
681 return templ_7745c5c3_Err707 return templ_7745c5c3_Err
682 }708 }
@@ -685,69 +711,69 @@ func refRow(kind string, r RefRow) templ.Component {
685 if templ_7745c5c3_Err != nil {711 if templ_7745c5c3_Err != nil {
686 return templ_7745c5c3_Err712 return templ_7745c5c3_Err
687 }713 }
688- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 52, "</td><td class=\"c-sub\">")714+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "</td><td class=\"c-sub\">")
689 if templ_7745c5c3_Err != nil {715 if templ_7745c5c3_Err != nil {
690 return templ_7745c5c3_Err716 return templ_7745c5c3_Err
691 }717 }
692- var templ_7745c5c3_Var31 string718+ var templ_7745c5c3_Var33 string
693- templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(r.Subject)719+ templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(r.Subject)
694 if templ_7745c5c3_Err != nil {720 if templ_7745c5c3_Err != nil {
695- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 152, Col: 31}721+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 153, Col: 31}
696 }722 }
697- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))723+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
698 if templ_7745c5c3_Err != nil {724 if templ_7745c5c3_Err != nil {
699 return templ_7745c5c3_Err725 return templ_7745c5c3_Err
700 }726 }
701- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 53, "</td><td class=\"c-when dim\">")727+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "</td><td class=\"c-when dim\">")
702 if templ_7745c5c3_Err != nil {728 if templ_7745c5c3_Err != nil {
703 return templ_7745c5c3_Err729 return templ_7745c5c3_Err
704 }730 }
705 if r.WhenAt != "" {731 if r.WhenAt != "" {
706- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 54, "<time datetime=\"")732+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "<time datetime=\"")
707 if templ_7745c5c3_Err != nil {733 if templ_7745c5c3_Err != nil {
708 return templ_7745c5c3_Err734 return templ_7745c5c3_Err
709 }735 }
710- var templ_7745c5c3_Var32 string736+ var templ_7745c5c3_Var34 string
711- templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)737+ templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
712 if templ_7745c5c3_Err != nil {738 if templ_7745c5c3_Err != nil {
713- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 155, Col: 29}739+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 156, Col: 29}
714 }740 }
715- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)741+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
716 if templ_7745c5c3_Err != nil {742 if templ_7745c5c3_Err != nil {
717 return templ_7745c5c3_Err743 return templ_7745c5c3_Err
718 }744 }
719- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "\" title=\"")745+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "\" title=\"")
720 if templ_7745c5c3_Err != nil {746 if templ_7745c5c3_Err != nil {
721 return templ_7745c5c3_Err747 return templ_7745c5c3_Err
722 }748 }
723- var templ_7745c5c3_Var33 string749+ var templ_7745c5c3_Var35 string
724- templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)750+ templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.WhenAt)
725 if templ_7745c5c3_Err != nil {751 if templ_7745c5c3_Err != nil {
726- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 155, Col: 48}752+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 156, Col: 48}
727 }753 }
728- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)754+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
729 if templ_7745c5c3_Err != nil {755 if templ_7745c5c3_Err != nil {
730 return templ_7745c5c3_Err756 return templ_7745c5c3_Err
731 }757 }
732- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 56, "\">")758+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "\">")
733 if templ_7745c5c3_Err != nil {759 if templ_7745c5c3_Err != nil {
734 return templ_7745c5c3_Err760 return templ_7745c5c3_Err
735 }761 }
736- var templ_7745c5c3_Var34 string762+ var templ_7745c5c3_Var36 string
737- templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(r.When)763+ templ_7745c5c3_Var36, templ_7745c5c3_Err = templ.JoinStringErrs(r.When)
738 if templ_7745c5c3_Err != nil {764 if templ_7745c5c3_Err != nil {
739- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 155, Col: 59}765+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 156, Col: 59}
740 }766 }
741- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))767+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var36))
742 if templ_7745c5c3_Err != nil {768 if templ_7745c5c3_Err != nil {
743 return templ_7745c5c3_Err769 return templ_7745c5c3_Err
744 }770 }
745- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "</time>")771+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "</time>")
746 if templ_7745c5c3_Err != nil {772 if templ_7745c5c3_Err != nil {
747 return templ_7745c5c3_Err773 return templ_7745c5c3_Err
748 }774 }
749 }775 }
750- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, "</td></tr>")776+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</td></tr>")
751 if templ_7745c5c3_Err != nil {777 if templ_7745c5c3_Err != nil {
752 return templ_7745c5c3_Err778 return templ_7745c5c3_Err
753 }779 }
@@ -771,12 +797,12 @@ func refsRail(p RefsPage) templ.Component {
771 }()797 }()
772 }798 }
773 ctx = templ.InitializeContext(ctx)799 ctx = templ.InitializeContext(ctx)
774- templ_7745c5c3_Var35 := templ.GetChildren(ctx)800+ templ_7745c5c3_Var37 := templ.GetChildren(ctx)
775- if templ_7745c5c3_Var35 == nil {801+ if templ_7745c5c3_Var37 == nil {
776- templ_7745c5c3_Var35 = templ.NopComponent802+ templ_7745c5c3_Var37 = templ.NopComponent
777 }803 }
778 ctx = templ.ClearChildren(ctx)804 ctx = templ.ClearChildren(ctx)
779- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, "<section aria-labelledby=\"r-refs\"><h2 id=\"r-refs\">Overview</h2>")805+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<section aria-labelledby=\"r-refs\"><h2 id=\"r-refs\">Overview</h2>")
780 if templ_7745c5c3_Err != nil {806 if templ_7745c5c3_Err != nil {
781 return templ_7745c5c3_Err807 return templ_7745c5c3_Err
782 }808 }
@@ -784,7 +810,7 @@ func refsRail(p RefsPage) templ.Component {
784 if templ_7745c5c3_Err != nil {810 if templ_7745c5c3_Err != nil {
785 return templ_7745c5c3_Err811 return templ_7745c5c3_Err
786 }812 }
787- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 60, "</section>")813+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</section>")
788 if templ_7745c5c3_Err != nil {814 if templ_7745c5c3_Err != nil {
789 return templ_7745c5c3_Err815 return templ_7745c5c3_Err
790 }816 }
@@ -809,12 +835,12 @@ func State(l Layout, p StatePage) templ.Component {
809 }()835 }()
810 }836 }
811 ctx = templ.InitializeContext(ctx)837 ctx = templ.InitializeContext(ctx)
812- templ_7745c5c3_Var36 := templ.GetChildren(ctx)838+ templ_7745c5c3_Var38 := templ.GetChildren(ctx)
813- if templ_7745c5c3_Var36 == nil {839+ if templ_7745c5c3_Var38 == nil {
814- templ_7745c5c3_Var36 = templ.NopComponent840+ templ_7745c5c3_Var38 = templ.NopComponent
815 }841 }
816 ctx = templ.ClearChildren(ctx)842 ctx = templ.ClearChildren(ctx)
817- templ_7745c5c3_Var37 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {843+ templ_7745c5c3_Var39 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
818 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context844 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
819 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)845 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
820 if !templ_7745c5c3_IsBuffer {846 if !templ_7745c5c3_IsBuffer {
@@ -826,51 +852,51 @@ func State(l Layout, p StatePage) templ.Component {
826 }()852 }()
827 }853 }
828 ctx = templ.InitializeContext(ctx)854 ctx = templ.InitializeContext(ctx)
829- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "<section class=\"state\" aria-labelledby=\"state-title\"><p class=\"eyebrow\">")855+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "<section class=\"state\" aria-labelledby=\"state-title\"><p class=\"eyebrow\">")
830 if templ_7745c5c3_Err != nil {856 if templ_7745c5c3_Err != nil {
831 return templ_7745c5c3_Err857 return templ_7745c5c3_Err
832 }858 }
833- var templ_7745c5c3_Var38 string859+ var templ_7745c5c3_Var40 string
834- templ_7745c5c3_Var38, templ_7745c5c3_Err = templ.JoinStringErrs(p.Code)860+ templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(p.Code)
835 if templ_7745c5c3_Err != nil {861 if templ_7745c5c3_Err != nil {
836- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 172, Col: 30}862+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 173, Col: 30}
837 }863 }
838- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var38))864+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
839 if templ_7745c5c3_Err != nil {865 if templ_7745c5c3_Err != nil {
840 return templ_7745c5c3_Err866 return templ_7745c5c3_Err
841 }867 }
842- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "</p><h1 id=\"state-title\">")868+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "</p><h1 id=\"state-title\">")
843 if templ_7745c5c3_Err != nil {869 if templ_7745c5c3_Err != nil {
844 return templ_7745c5c3_Err870 return templ_7745c5c3_Err
845 }871 }
846- var templ_7745c5c3_Var39 string872+ var templ_7745c5c3_Var41 string
847- templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)873+ templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
848 if templ_7745c5c3_Err != nil {874 if templ_7745c5c3_Err != nil {
849- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 173, Col: 33}875+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 174, Col: 33}
850 }876 }
851- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39))877+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))
852 if templ_7745c5c3_Err != nil {878 if templ_7745c5c3_Err != nil {
853 return templ_7745c5c3_Err879 return templ_7745c5c3_Err
854 }880 }
855- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "</h1>")881+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</h1>")
856 if templ_7745c5c3_Err != nil {882 if templ_7745c5c3_Err != nil {
857 return templ_7745c5c3_Err883 return templ_7745c5c3_Err
858 }884 }
859 for _, line := range p.Lines {885 for _, line := range p.Lines {
860- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 64, "<p class=\"dim\">")886+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<p class=\"dim\">")
861 if templ_7745c5c3_Err != nil {887 if templ_7745c5c3_Err != nil {
862 return templ_7745c5c3_Err888 return templ_7745c5c3_Err
863 }889 }
864- var templ_7745c5c3_Var40 string890+ var templ_7745c5c3_Var42 string
865- templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(line)891+ templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(line)
866 if templ_7745c5c3_Err != nil {892 if templ_7745c5c3_Err != nil {
867- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 175, Col: 25}893+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 176, Col: 25}
868 }894 }
869- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))895+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))
870 if templ_7745c5c3_Err != nil {896 if templ_7745c5c3_Err != nil {
871 return templ_7745c5c3_Err897 return templ_7745c5c3_Err
872 }898 }
873- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 65, "</p>")899+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "</p>")
874 if templ_7745c5c3_Err != nil {900 if templ_7745c5c3_Err != nil {
875 return templ_7745c5c3_Err901 return templ_7745c5c3_Err
876 }902 }
@@ -881,49 +907,49 @@ func State(l Layout, p StatePage) templ.Component {
881 return templ_7745c5c3_Err907 return templ_7745c5c3_Err
882 }908 }
883 }909 }
884- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 66, "<p class=\"state-back\">")910+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "<p class=\"state-back\">")
885 if templ_7745c5c3_Err != nil {911 if templ_7745c5c3_Err != nil {
886 return templ_7745c5c3_Err912 return templ_7745c5c3_Err
887 }913 }
888 for _, b := range p.Back {914 for _, b := range p.Back {
889- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 67, "<a href=\"")915+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "<a href=\"")
890 if templ_7745c5c3_Err != nil {916 if templ_7745c5c3_Err != nil {
891 return templ_7745c5c3_Err917 return templ_7745c5c3_Err
892 }918 }
893- var templ_7745c5c3_Var41 templ.SafeURL919+ var templ_7745c5c3_Var43 templ.SafeURL
894- templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(b.Href))920+ templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(b.Href))
895 if templ_7745c5c3_Err != nil {921 if templ_7745c5c3_Err != nil {
896- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 182, Col: 36}922+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 183, Col: 36}
897 }923 }
898- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41))924+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
899 if templ_7745c5c3_Err != nil {925 if templ_7745c5c3_Err != nil {
900 return templ_7745c5c3_Err926 return templ_7745c5c3_Err
901 }927 }
902- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 68, "\">")928+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "\">")
903 if templ_7745c5c3_Err != nil {929 if templ_7745c5c3_Err != nil {
904 return templ_7745c5c3_Err930 return templ_7745c5c3_Err
905 }931 }
906- var templ_7745c5c3_Var42 string932+ var templ_7745c5c3_Var44 string
907- templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(b.Label)933+ templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(b.Label)
908 if templ_7745c5c3_Err != nil {934 if templ_7745c5c3_Err != nil {
909- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 182, Col: 48}935+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 183, Col: 48}
910 }936 }
911- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42))937+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44))
912 if templ_7745c5c3_Err != nil {938 if templ_7745c5c3_Err != nil {
913 return templ_7745c5c3_Err939 return templ_7745c5c3_Err
914 }940 }
915- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "</a>")941+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "</a>")
916 if templ_7745c5c3_Err != nil {942 if templ_7745c5c3_Err != nil {
917 return templ_7745c5c3_Err943 return templ_7745c5c3_Err
918 }944 }
919 }945 }
920- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, "</p></section>")946+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</p></section>")
921 if templ_7745c5c3_Err != nil {947 if templ_7745c5c3_Err != nil {
922 return templ_7745c5c3_Err948 return templ_7745c5c3_Err
923 }949 }
924 return nil950 return nil
925 })951 })
926- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var37), templ_7745c5c3_Buffer)952+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var39), templ_7745c5c3_Buffer)
927 if templ_7745c5c3_Err != nil {953 if templ_7745c5c3_Err != nil {
928 return templ_7745c5c3_Err954 return templ_7745c5c3_Err
929 }955 }
@@ -952,12 +978,12 @@ func Login(l Layout, p LoginPage) templ.Component {
952 }()978 }()
953 }979 }
954 ctx = templ.InitializeContext(ctx)980 ctx = templ.InitializeContext(ctx)
955- templ_7745c5c3_Var43 := templ.GetChildren(ctx)981+ templ_7745c5c3_Var45 := templ.GetChildren(ctx)
956- if templ_7745c5c3_Var43 == nil {982+ if templ_7745c5c3_Var45 == nil {
957- templ_7745c5c3_Var43 = templ.NopComponent983+ templ_7745c5c3_Var45 = templ.NopComponent
958 }984 }
959 ctx = templ.ClearChildren(ctx)985 ctx = templ.ClearChildren(ctx)
960- templ_7745c5c3_Var44 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {986+ templ_7745c5c3_Var46 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
961 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context987 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
962 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)988 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
963 if !templ_7745c5c3_IsBuffer {989 if !templ_7745c5c3_IsBuffer {
@@ -969,75 +995,75 @@ func Login(l Layout, p LoginPage) templ.Component {
969 }()995 }()
970 }996 }
971 ctx = templ.InitializeContext(ctx)997 ctx = templ.InitializeContext(ctx)
972- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")998+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")
973 if templ_7745c5c3_Err != nil {999 if templ_7745c5c3_Err != nil {
974 return templ_7745c5c3_Err1000 return templ_7745c5c3_Err
975 }1001 }
976- var templ_7745c5c3_Var45 string1002+ var templ_7745c5c3_Var47 string
977- templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)1003+ templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
978 if templ_7745c5c3_Err != nil {1004 if templ_7745c5c3_Err != nil {
979- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 197, Col: 33}1005+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 198, Col: 33}
980 }1006 }
981- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45))1007+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))
982 if templ_7745c5c3_Err != nil {1008 if templ_7745c5c3_Err != nil {
983 return templ_7745c5c3_Err1009 return templ_7745c5c3_Err
984 }1010 }
985- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, "</h1>")1011+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</h1>")
986 if templ_7745c5c3_Err != nil {1012 if templ_7745c5c3_Err != nil {
987 return templ_7745c5c3_Err1013 return templ_7745c5c3_Err
988 }1014 }
989 for _, line := range p.Lines {1015 for _, line := range p.Lines {
990- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, "<p class=\"dim\">")1016+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<p class=\"dim\">")
991 if templ_7745c5c3_Err != nil {1017 if templ_7745c5c3_Err != nil {
992 return templ_7745c5c3_Err1018 return templ_7745c5c3_Err
993 }1019 }
994- var templ_7745c5c3_Var46 string1020+ var templ_7745c5c3_Var48 string
995- templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinStringErrs(line)1021+ templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(line)
996 if templ_7745c5c3_Err != nil {1022 if templ_7745c5c3_Err != nil {
997- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 199, Col: 25}1023+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 200, Col: 25}
998 }1024 }
999- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46))1025+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))
1000 if templ_7745c5c3_Err != nil {1026 if templ_7745c5c3_Err != nil {
1001 return templ_7745c5c3_Err1027 return templ_7745c5c3_Err
1002 }1028 }
1003- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, "</p>")1029+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "</p>")
1004 if templ_7745c5c3_Err != nil {1030 if templ_7745c5c3_Err != nil {
1005 return templ_7745c5c3_Err1031 return templ_7745c5c3_Err
1006 }1032 }
1007 }1033 }
1008- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "<form class=\"state-act\" method=\"post\" action=\"")1034+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "<form class=\"state-act\" method=\"post\" action=\"")
1009 if templ_7745c5c3_Err != nil {1035 if templ_7745c5c3_Err != nil {
1010 return templ_7745c5c3_Err1036 return templ_7745c5c3_Err
1011 }1037 }
1012- var templ_7745c5c3_Var47 templ.SafeURL1038+ var templ_7745c5c3_Var49 templ.SafeURL
1013- templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Action))1039+ templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(p.Action))
1014 if templ_7745c5c3_Err != nil {1040 if templ_7745c5c3_Err != nil {
1015- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 201, Col: 73}1041+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 202, Col: 73}
1016 }1042 }
1017- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47))1043+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49))
1018 if templ_7745c5c3_Err != nil {1044 if templ_7745c5c3_Err != nil {
1019 return templ_7745c5c3_Err1045 return templ_7745c5c3_Err
1020 }1046 }
1021- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\" data-autosubmit><button type=\"submit\" class=\"btn primary\">")1047+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "\" data-autosubmit><button type=\"submit\" class=\"btn primary\">")
1022 if templ_7745c5c3_Err != nil {1048 if templ_7745c5c3_Err != nil {
1023 return templ_7745c5c3_Err1049 return templ_7745c5c3_Err
1024 }1050 }
1025- var templ_7745c5c3_Var48 string1051+ var templ_7745c5c3_Var50 string
1026- templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)1052+ templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)
1027 if templ_7745c5c3_Err != nil {1053 if templ_7745c5c3_Err != nil {
1028- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 202, Col: 56}1054+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 203, Col: 56}
1029 }1055 }
1030- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48))1056+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50))
1031 if templ_7745c5c3_Err != nil {1057 if templ_7745c5c3_Err != nil {
1032 return templ_7745c5c3_Err1058 return templ_7745c5c3_Err
1033 }1059 }
1034- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, "</button></form><p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")1060+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</button></form><p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")
1035 if templ_7745c5c3_Err != nil {1061 if templ_7745c5c3_Err != nil {
1036 return templ_7745c5c3_Err1062 return templ_7745c5c3_Err
1037 }1063 }
1038 return nil1064 return nil
1039 })1065 })
1040- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var44), templ_7745c5c3_Buffer)1066+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var46), templ_7745c5c3_Buffer)
1041 if templ_7745c5c3_Err != nil {1067 if templ_7745c5c3_Err != nil {
1042 return templ_7745c5c3_Err1068 return templ_7745c5c3_Err
1043 }1069 }
@@ -1063,12 +1089,12 @@ func PasskeyLogin(l Layout, p PasskeyLoginPage) templ.Component {
1063 }()1089 }()
1064 }1090 }
1065 ctx = templ.InitializeContext(ctx)1091 ctx = templ.InitializeContext(ctx)
1066- templ_7745c5c3_Var49 := templ.GetChildren(ctx)1092+ templ_7745c5c3_Var51 := templ.GetChildren(ctx)
1067- if templ_7745c5c3_Var49 == nil {1093+ if templ_7745c5c3_Var51 == nil {
1068- templ_7745c5c3_Var49 = templ.NopComponent1094+ templ_7745c5c3_Var51 = templ.NopComponent
1069 }1095 }
1070 ctx = templ.ClearChildren(ctx)1096 ctx = templ.ClearChildren(ctx)
1071- templ_7745c5c3_Var50 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {1097+ templ_7745c5c3_Var52 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1072 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context1098 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
1073 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)1099 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
1074 if !templ_7745c5c3_IsBuffer {1100 if !templ_7745c5c3_IsBuffer {
@@ -1080,88 +1106,88 @@ func PasskeyLogin(l Layout, p PasskeyLoginPage) templ.Component {
1080 }()1106 }()
1081 }1107 }
1082 ctx = templ.InitializeContext(ctx)1108 ctx = templ.InitializeContext(ctx)
1083- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")1109+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<section class=\"state\" aria-labelledby=\"state-title\"><h1 id=\"state-title\">")
1084 if templ_7745c5c3_Err != nil {1110 if templ_7745c5c3_Err != nil {
1085 return templ_7745c5c3_Err1111 return templ_7745c5c3_Err
1086 }1112 }
1087- var templ_7745c5c3_Var51 string1113+ var templ_7745c5c3_Var53 string
1088- templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)1114+ templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.JoinStringErrs(p.Title)
1089 if templ_7745c5c3_Err != nil {1115 if templ_7745c5c3_Err != nil {
1090- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 214, Col: 33}1116+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 215, Col: 33}
1091 }1117 }
1092- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51))1118+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var53))
1093 if templ_7745c5c3_Err != nil {1119 if templ_7745c5c3_Err != nil {
1094 return templ_7745c5c3_Err1120 return templ_7745c5c3_Err
1095 }1121 }
1096- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "</h1><form class=\"state-act\" data-js data-passkey-form><button type=\"submit\" class=\"btn primary\" data-passkey=\"login\" data-begin=\"")1122+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</h1><form class=\"state-act\" data-js data-passkey-form><button type=\"submit\" class=\"btn primary\" data-passkey=\"login\" data-begin=\"")
1097 if templ_7745c5c3_Err != nil {1123 if templ_7745c5c3_Err != nil {
1098 return templ_7745c5c3_Err1124 return templ_7745c5c3_Err
1099 }1125 }
1100- var templ_7745c5c3_Var52 string1126+ var templ_7745c5c3_Var54 string
1101- templ_7745c5c3_Var52, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)1127+ templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)
1102 if templ_7745c5c3_Err != nil {1128 if templ_7745c5c3_Err != nil {
1103- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 216, Col: 87}1129+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 217, Col: 87}
1104 }1130 }
1105- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var52)1131+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var54)
1106 if templ_7745c5c3_Err != nil {1132 if templ_7745c5c3_Err != nil {
1107 return templ_7745c5c3_Err1133 return templ_7745c5c3_Err
1108 }1134 }
1109- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "\" data-finish=\"")1135+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "\" data-finish=\"")
1110 if templ_7745c5c3_Err != nil {1136 if templ_7745c5c3_Err != nil {
1111 return templ_7745c5c3_Err1137 return templ_7745c5c3_Err
1112 }1138 }
1113- var templ_7745c5c3_Var53 string1139+ var templ_7745c5c3_Var55 string
1114- templ_7745c5c3_Var53, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)1140+ templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)
1115 if templ_7745c5c3_Err != nil {1141 if templ_7745c5c3_Err != nil {
1116- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 216, Col: 112}1142+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 217, Col: 112}
1117 }1143 }
1118- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var53)1144+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var55)
1119 if templ_7745c5c3_Err != nil {1145 if templ_7745c5c3_Err != nil {
1120 return templ_7745c5c3_Err1146 return templ_7745c5c3_Err
1121 }1147 }
1122- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "\">")1148+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "\">")
1123 if templ_7745c5c3_Err != nil {1149 if templ_7745c5c3_Err != nil {
1124 return templ_7745c5c3_Err1150 return templ_7745c5c3_Err
1125 }1151 }
1126- var templ_7745c5c3_Var54 string1152+ var templ_7745c5c3_Var56 string
1127- templ_7745c5c3_Var54, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)1153+ templ_7745c5c3_Var56, templ_7745c5c3_Err = templ.JoinStringErrs(p.Button)
1128 if templ_7745c5c3_Err != nil {1154 if templ_7745c5c3_Err != nil {
1129- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 216, Col: 125}1155+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 217, Col: 125}
1130 }1156 }
1131- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var54))1157+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var56))
1132 if templ_7745c5c3_Err != nil {1158 if templ_7745c5c3_Err != nil {
1133 return templ_7745c5c3_Err1159 return templ_7745c5c3_Err
1134 }1160 }
1135- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</button></form><p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")1161+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</button></form><p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")
1136 if templ_7745c5c3_Err != nil {1162 if templ_7745c5c3_Err != nil {
1137 return templ_7745c5c3_Err1163 return templ_7745c5c3_Err
1138 }1164 }
1139 for _, line := range p.Lines {1165 for _, line := range p.Lines {
1140- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<p class=\"dim\">")1166+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<p class=\"dim\">")
1141 if templ_7745c5c3_Err != nil {1167 if templ_7745c5c3_Err != nil {
1142 return templ_7745c5c3_Err1168 return templ_7745c5c3_Err
1143 }1169 }
1144- var templ_7745c5c3_Var55 string1170+ var templ_7745c5c3_Var57 string
1145- templ_7745c5c3_Var55, templ_7745c5c3_Err = templ.JoinStringErrs(line)1171+ templ_7745c5c3_Var57, templ_7745c5c3_Err = templ.JoinStringErrs(line)
1146 if templ_7745c5c3_Err != nil {1172 if templ_7745c5c3_Err != nil {
1147- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 220, Col: 25}1173+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 221, Col: 25}
1148 }1174 }
1149- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var55))1175+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var57))
1150 if templ_7745c5c3_Err != nil {1176 if templ_7745c5c3_Err != nil {
1151 return templ_7745c5c3_Err1177 return templ_7745c5c3_Err
1152 }1178 }
1153- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</p>")1179+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</p>")
1154 if templ_7745c5c3_Err != nil {1180 if templ_7745c5c3_Err != nil {
1155 return templ_7745c5c3_Err1181 return templ_7745c5c3_Err
1156 }1182 }
1157 }1183 }
1158- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "<p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")1184+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<p class=\"state-back\"><a href=\"/\">Back to home</a></p></section>")
1159 if templ_7745c5c3_Err != nil {1185 if templ_7745c5c3_Err != nil {
1160 return templ_7745c5c3_Err1186 return templ_7745c5c3_Err
1161 }1187 }
1162 return nil1188 return nil
1163 })1189 })
1164- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var50), templ_7745c5c3_Buffer)1190+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var52), templ_7745c5c3_Buffer)
1165 if templ_7745c5c3_Err != nil {1191 if templ_7745c5c3_Err != nil {
1166 return templ_7745c5c3_Err1192 return templ_7745c5c3_Err
1167 }1193 }
@@ -1186,12 +1212,12 @@ func Passkeys(l Layout, p PasskeysPage) templ.Component {
1186 }()1212 }()
1187 }1213 }
1188 ctx = templ.InitializeContext(ctx)1214 ctx = templ.InitializeContext(ctx)
1189- templ_7745c5c3_Var56 := templ.GetChildren(ctx)1215+ templ_7745c5c3_Var58 := templ.GetChildren(ctx)
1190- if templ_7745c5c3_Var56 == nil {1216+ if templ_7745c5c3_Var58 == nil {
1191- templ_7745c5c3_Var56 = templ.NopComponent1217+ templ_7745c5c3_Var58 = templ.NopComponent
1192 }1218 }
1193 ctx = templ.ClearChildren(ctx)1219 ctx = templ.ClearChildren(ctx)
1194- templ_7745c5c3_Var57 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {1220+ templ_7745c5c3_Var59 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
1195 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context1221 templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
1196 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)1222 templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
1197 if !templ_7745c5c3_IsBuffer {1223 if !templ_7745c5c3_IsBuffer {
@@ -1203,231 +1229,231 @@ func Passkeys(l Layout, p PasskeysPage) templ.Component {
1203 }()1229 }()
1204 }1230 }
1205 ctx = templ.InitializeContext(ctx)1231 ctx = templ.InitializeContext(ctx)
1206- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<h2 class=\"rule-h\"><span>Passkeys</span><span class=\"count\">")1232+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<h2 class=\"rule-h\"><span>Passkeys</span><span class=\"count\">")
1207 if templ_7745c5c3_Err != nil {1233 if templ_7745c5c3_Err != nil {
1208 return templ_7745c5c3_Err1234 return templ_7745c5c3_Err
1209 }1235 }
1210- var templ_7745c5c3_Var58 string1236+ var templ_7745c5c3_Var60 string
1211- templ_7745c5c3_Var58, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Rows)))1237+ templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(len(p.Rows)))
1212 if templ_7745c5c3_Err != nil {1238 if templ_7745c5c3_Err != nil {
1213- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 230, Col: 81}1239+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 231, Col: 81}
1214 }1240 }
1215- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var58))1241+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var60))
1216 if templ_7745c5c3_Err != nil {1242 if templ_7745c5c3_Err != nil {
1217 return templ_7745c5c3_Err1243 return templ_7745c5c3_Err
1218 }1244 }
1219- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</span></h2><p class=\"dim\">A passkey signs you in from this device or your phone, without a terminal. Removing one signs every browser out.</p>")1245+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "</span></h2><p class=\"dim\">A passkey signs you in from this device or your phone, without a terminal. Removing one signs every browser out.</p>")
1220 if templ_7745c5c3_Err != nil {1246 if templ_7745c5c3_Err != nil {
1221 return templ_7745c5c3_Err1247 return templ_7745c5c3_Err
1222 }1248 }
1223 if len(p.Rows) > 0 {1249 if len(p.Rows) > 0 {
1224- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "<table class=\"list passkeys\" aria-label=\"Passkeys\"><thead><tr><th class=\"cur\"></th><th class=\"c-name\">Name</th><th class=\"c-when\">Created</th><th class=\"c-when\">Last used</th><th class=\"c-sync\">Synced</th><th class=\"c-act\"><span class=\"sr\">Remove</span></th></tr></thead> <tbody>")1250+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<table class=\"list passkeys\" aria-label=\"Passkeys\"><thead><tr><th class=\"cur\"></th><th class=\"c-name\">Name</th><th class=\"c-when\">Created</th><th class=\"c-when\">Last used</th><th class=\"c-sync\">Synced</th><th class=\"c-act\"><span class=\"sr\">Remove</span></th></tr></thead> <tbody>")
1225 if templ_7745c5c3_Err != nil {1251 if templ_7745c5c3_Err != nil {
1226 return templ_7745c5c3_Err1252 return templ_7745c5c3_Err
1227 }1253 }
1228 for _, r := range p.Rows {1254 for _, r := range p.Rows {
1229- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<tr><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-name\">")1255+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "<tr><td class=\"cur\" aria-hidden=\"true\"></td><td class=\"c-name\">")
1230 if templ_7745c5c3_Err != nil {1256 if templ_7745c5c3_Err != nil {
1231 return templ_7745c5c3_Err1257 return templ_7745c5c3_Err
1232 }1258 }
1233- var templ_7745c5c3_Var59 string1259+ var templ_7745c5c3_Var61 string
1234- templ_7745c5c3_Var59, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)1260+ templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(r.Name)
1235 if templ_7745c5c3_Err != nil {1261 if templ_7745c5c3_Err != nil {
1236- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 248, Col: 34}1262+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 249, Col: 34}
1237 }1263 }
1238- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var59))1264+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))
1239 if templ_7745c5c3_Err != nil {1265 if templ_7745c5c3_Err != nil {
1240 return templ_7745c5c3_Err1266 return templ_7745c5c3_Err
1241 }1267 }
1242- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</td><td class=\"c-when nowrap\"><time")1268+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "</td><td class=\"c-when nowrap\"><time")
1243 if templ_7745c5c3_Err != nil {1269 if templ_7745c5c3_Err != nil {
1244 return templ_7745c5c3_Err1270 return templ_7745c5c3_Err
1245 }1271 }
1246 if r.CreatedAt != "" {1272 if r.CreatedAt != "" {
1247- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, " datetime=\"")1273+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, " datetime=\"")
1248 if templ_7745c5c3_Err != nil {1274 if templ_7745c5c3_Err != nil {
1249 return templ_7745c5c3_Err1275 return templ_7745c5c3_Err
1250 }1276 }
1251- var templ_7745c5c3_Var60 string1277+ var templ_7745c5c3_Var62 string
1252- templ_7745c5c3_Var60, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CreatedAt)1278+ templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.CreatedAt)
1253 if templ_7745c5c3_Err != nil {1279 if templ_7745c5c3_Err != nil {
1254- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 252, Col: 32}1280+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 253, Col: 32}
1255 }1281 }
1256- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var60)1282+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var62)
1257 if templ_7745c5c3_Err != nil {1283 if templ_7745c5c3_Err != nil {
1258 return templ_7745c5c3_Err1284 return templ_7745c5c3_Err
1259 }1285 }
1260- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 92, "\"")1286+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "\"")
1261 if templ_7745c5c3_Err != nil {1287 if templ_7745c5c3_Err != nil {
1262 return templ_7745c5c3_Err1288 return templ_7745c5c3_Err
1263 }1289 }
1264 }1290 }
1265- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 93, ">")1291+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, ">")
1266 if templ_7745c5c3_Err != nil {1292 if templ_7745c5c3_Err != nil {
1267 return templ_7745c5c3_Err1293 return templ_7745c5c3_Err
1268 }1294 }
1269- var templ_7745c5c3_Var61 string1295+ var templ_7745c5c3_Var63 string
1270- templ_7745c5c3_Var61, templ_7745c5c3_Err = templ.JoinStringErrs(r.Created)1296+ templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(r.Created)
1271 if templ_7745c5c3_Err != nil {1297 if templ_7745c5c3_Err != nil {
1272- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 254, Col: 20}1298+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 255, Col: 20}
1273 }1299 }
1274- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var61))1300+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))
1275 if templ_7745c5c3_Err != nil {1301 if templ_7745c5c3_Err != nil {
1276 return templ_7745c5c3_Err1302 return templ_7745c5c3_Err
1277 }1303 }
1278- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "</time></td><td class=\"c-when nowrap\"><time")1304+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "</time></td><td class=\"c-when nowrap\"><time")
1279 if templ_7745c5c3_Err != nil {1305 if templ_7745c5c3_Err != nil {
1280 return templ_7745c5c3_Err1306 return templ_7745c5c3_Err
1281 }1307 }
1282 if r.LastUsedAt != "" {1308 if r.LastUsedAt != "" {
1283- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, " datetime=\"")1309+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, " datetime=\"")
1284 if templ_7745c5c3_Err != nil {1310 if templ_7745c5c3_Err != nil {
1285 return templ_7745c5c3_Err1311 return templ_7745c5c3_Err
1286 }1312 }
1287- var templ_7745c5c3_Var62 string1313+ var templ_7745c5c3_Var64 string
1288- templ_7745c5c3_Var62, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.LastUsedAt)1314+ templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.ResolveAttributeValue(r.LastUsedAt)
1289 if templ_7745c5c3_Err != nil {1315 if templ_7745c5c3_Err != nil {
1290- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 259, Col: 33}1316+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 260, Col: 33}
1291 }1317 }
1292- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var62)1318+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var64)
1293 if templ_7745c5c3_Err != nil {1319 if templ_7745c5c3_Err != nil {
1294 return templ_7745c5c3_Err1320 return templ_7745c5c3_Err
1295 }1321 }
1296- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, "\"")1322+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "\"")
1297 if templ_7745c5c3_Err != nil {1323 if templ_7745c5c3_Err != nil {
1298 return templ_7745c5c3_Err1324 return templ_7745c5c3_Err
1299 }1325 }
1300 }1326 }
1301- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, ">")1327+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, ">")
1302 if templ_7745c5c3_Err != nil {1328 if templ_7745c5c3_Err != nil {
1303 return templ_7745c5c3_Err1329 return templ_7745c5c3_Err
1304 }1330 }
1305- var templ_7745c5c3_Var63 string1331+ var templ_7745c5c3_Var65 string
1306- templ_7745c5c3_Var63, templ_7745c5c3_Err = templ.JoinStringErrs(r.LastUsed)1332+ templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.JoinStringErrs(r.LastUsed)
1307 if templ_7745c5c3_Err != nil {1333 if templ_7745c5c3_Err != nil {
1308- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 261, Col: 21}1334+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 262, Col: 21}
1309 }1335 }
1310- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var63))1336+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var65))
1311 if templ_7745c5c3_Err != nil {1337 if templ_7745c5c3_Err != nil {
1312 return templ_7745c5c3_Err1338 return templ_7745c5c3_Err
1313 }1339 }
1314- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 98, "</time></td><td class=\"c-sync\">")1340+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "</time></td><td class=\"c-sync\">")
1315 if templ_7745c5c3_Err != nil {1341 if templ_7745c5c3_Err != nil {
1316 return templ_7745c5c3_Err1342 return templ_7745c5c3_Err
1317 }1343 }
1318 if r.Synced {1344 if r.Synced {
1319- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 99, "yes")1345+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "yes")
1320 if templ_7745c5c3_Err != nil {1346 if templ_7745c5c3_Err != nil {
1321 return templ_7745c5c3_Err1347 return templ_7745c5c3_Err
1322 }1348 }
1323 } else {1349 } else {
1324- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 100, "no")1350+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "no")
1325 if templ_7745c5c3_Err != nil {1351 if templ_7745c5c3_Err != nil {
1326 return templ_7745c5c3_Err1352 return templ_7745c5c3_Err
1327 }1353 }
1328 }1354 }
1329- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 101, "</td><td class=\"c-act\"><form method=\"post\" action=\"")1355+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "</td><td class=\"c-act\"><form method=\"post\" action=\"")
1330 if templ_7745c5c3_Err != nil {1356 if templ_7745c5c3_Err != nil {
1331 return templ_7745c5c3_Err1357 return templ_7745c5c3_Err
1332 }1358 }
1333- var templ_7745c5c3_Var64 templ.SafeURL1359+ var templ_7745c5c3_Var66 templ.SafeURL
1334- templ_7745c5c3_Var64, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Remove))1360+ templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(r.Remove))
1335 if templ_7745c5c3_Err != nil {1361 if templ_7745c5c3_Err != nil {
1336- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 271, Col: 60}1362+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 272, Col: 60}
1337 }1363 }
1338- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var64))1364+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))
1339 if templ_7745c5c3_Err != nil {1365 if templ_7745c5c3_Err != nil {
1340 return templ_7745c5c3_Err1366 return templ_7745c5c3_Err
1341 }1367 }
1342- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 102, "\"><button type=\"submit\" class=\"btn danger\" aria-label=\"")1368+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "\"><button type=\"submit\" class=\"btn danger\" aria-label=\"")
1343 if templ_7745c5c3_Err != nil {1369 if templ_7745c5c3_Err != nil {
1344 return templ_7745c5c3_Err1370 return templ_7745c5c3_Err
1345 }1371 }
1346- var templ_7745c5c3_Var65 string1372+ var templ_7745c5c3_Var67 string
1347- templ_7745c5c3_Var65, templ_7745c5c3_Err = templ.ResolveAttributeValue("Remove passkey " + r.Name)1373+ templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.ResolveAttributeValue("Remove passkey " + r.Name)
1348 if templ_7745c5c3_Err != nil {1374 if templ_7745c5c3_Err != nil {
1349- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 272, Col: 89}1375+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 273, Col: 89}
1350 }1376 }
1351- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var65)1377+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var67)
1352 if templ_7745c5c3_Err != nil {1378 if templ_7745c5c3_Err != nil {
1353 return templ_7745c5c3_Err1379 return templ_7745c5c3_Err
1354 }1380 }
1355- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 103, "\">Remove</button></form></td></tr>")1381+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "\">Remove</button></form></td></tr>")
1356 if templ_7745c5c3_Err != nil {1382 if templ_7745c5c3_Err != nil {
1357 return templ_7745c5c3_Err1383 return templ_7745c5c3_Err
1358 }1384 }
1359 }1385 }
1360- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 104, "</tbody></table>")1386+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, "</tbody></table>")
1361 if templ_7745c5c3_Err != nil {1387 if templ_7745c5c3_Err != nil {
1362 return templ_7745c5c3_Err1388 return templ_7745c5c3_Err
1363 }1389 }
1364 } else {1390 } else {
1365- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 105, "<p class=\"lede dim\">no passkeys yet.</p>")1391+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "<p class=\"lede dim\">no passkeys yet.</p>")
1366 if templ_7745c5c3_Err != nil {1392 if templ_7745c5c3_Err != nil {
1367 return templ_7745c5c3_Err1393 return templ_7745c5c3_Err
1368 }1394 }
1369 }1395 }
1370- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 106, " ")1396+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, " ")
1371 if templ_7745c5c3_Err != nil {1397 if templ_7745c5c3_Err != nil {
1372 return templ_7745c5c3_Err1398 return templ_7745c5c3_Err
1373 }1399 }
1374 if p.Full {1400 if p.Full {
1375- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 107, "<p class=\"dim\">")1401+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "<p class=\"dim\">")
1376 if templ_7745c5c3_Err != nil {1402 if templ_7745c5c3_Err != nil {
1377 return templ_7745c5c3_Err1403 return templ_7745c5c3_Err
1378 }1404 }
1379- var templ_7745c5c3_Var66 string1405+ var templ_7745c5c3_Var68 string
1380- templ_7745c5c3_Var66, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Max))1406+ templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.JoinStringErrs(itoa(p.Max))
1381 if templ_7745c5c3_Err != nil {1407 if templ_7745c5c3_Err != nil {
1382- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 283, Col: 31}1408+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 284, Col: 31}
1383 }1409 }
1384- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var66))1410+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var68))
1385 if templ_7745c5c3_Err != nil {1411 if templ_7745c5c3_Err != nil {
1386 return templ_7745c5c3_Err1412 return templ_7745c5c3_Err
1387 }1413 }
1388- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 108, " passkeys are registered, the most there can be. Remove one to add another.</p>")1414+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, " passkeys are registered, the most there can be. Remove one to add another.</p>")
1389 if templ_7745c5c3_Err != nil {1415 if templ_7745c5c3_Err != nil {
1390 return templ_7745c5c3_Err1416 return templ_7745c5c3_Err
1391 }1417 }
1392 } else {1418 } else {
1393- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 109, "<form class=\"passkey-add\" data-js data-passkey-form><label for=\"passkey-name\" class=\"sr\">Name of the new passkey</label> <input id=\"passkey-name\" type=\"text\" placeholder=\"Name, for example Pixel 9\" autocomplete=\"off\" data-passkey-name> <button type=\"submit\" class=\"btn primary\" data-passkey=\"register\" data-begin=\"")1419+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "<form class=\"passkey-add\" data-js data-passkey-form><label for=\"passkey-name\" class=\"sr\">Name of the new passkey</label> <input id=\"passkey-name\" type=\"text\" placeholder=\"Name, for example Pixel 9\" autocomplete=\"off\" data-passkey-name> <button type=\"submit\" class=\"btn primary\" data-passkey=\"register\" data-begin=\"")
1394 if templ_7745c5c3_Err != nil {1420 if templ_7745c5c3_Err != nil {
1395 return templ_7745c5c3_Err1421 return templ_7745c5c3_Err
1396 }1422 }
1397- var templ_7745c5c3_Var67 string1423+ var templ_7745c5c3_Var69 string
1398- templ_7745c5c3_Var67, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)1424+ templ_7745c5c3_Var69, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Begin)
1399 if templ_7745c5c3_Err != nil {1425 if templ_7745c5c3_Err != nil {
1400- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 288, Col: 90}1426+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 289, Col: 90}
1401 }1427 }
1402- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var67)1428+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var69)
1403 if templ_7745c5c3_Err != nil {1429 if templ_7745c5c3_Err != nil {
1404 return templ_7745c5c3_Err1430 return templ_7745c5c3_Err
1405 }1431 }
1406- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 110, "\" data-finish=\"")1432+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, "\" data-finish=\"")
1407 if templ_7745c5c3_Err != nil {1433 if templ_7745c5c3_Err != nil {
1408 return templ_7745c5c3_Err1434 return templ_7745c5c3_Err
1409 }1435 }
1410- var templ_7745c5c3_Var68 string1436+ var templ_7745c5c3_Var70 string
1411- templ_7745c5c3_Var68, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)1437+ templ_7745c5c3_Var70, templ_7745c5c3_Err = templ.ResolveAttributeValue(p.Finish)
1412 if templ_7745c5c3_Err != nil {1438 if templ_7745c5c3_Err != nil {
1413- return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 288, Col: 115}1439+ return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/pages.templ`, Line: 289, Col: 115}
1414 }1440 }
1415- _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var68)1441+ _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var70)
1416 if templ_7745c5c3_Err != nil {1442 if templ_7745c5c3_Err != nil {
1417 return templ_7745c5c3_Err1443 return templ_7745c5c3_Err
1418 }1444 }
1419- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 111, "\">Add a passkey</button></form>")1445+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 113, "\">Add a passkey</button></form>")
1420 if templ_7745c5c3_Err != nil {1446 if templ_7745c5c3_Err != nil {
1421 return templ_7745c5c3_Err1447 return templ_7745c5c3_Err
1422 }1448 }
1423 }1449 }
1424- templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 112, " <p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")1450+ templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 114, " <p class=\"dim\" role=\"status\" aria-live=\"polite\" data-passkey-status></p>")
1425 if templ_7745c5c3_Err != nil {1451 if templ_7745c5c3_Err != nil {
1426 return templ_7745c5c3_Err1452 return templ_7745c5c3_Err
1427 }1453 }
1428 return nil1454 return nil
1429 })1455 })
1430- templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var57), templ_7745c5c3_Buffer)1456+ templ_7745c5c3_Err = Shell(l, nil).Render(templ.WithChildren(ctx, templ_7745c5c3_Var59), templ_7745c5c3_Buffer)
1431 if templ_7745c5c3_Err != nil {1457 if templ_7745c5c3_Err != nil {
1432 return templ_7745c5c3_Err1458 return templ_7745c5c3_Err
1433 }1459 }
M internal/adapters/web/shell.templ +0 -10
@@ -128,16 +128,6 @@ templ Header(l Layout) {
128128 <span class="search-label">Go to…</span>
129129 <kbd aria-hidden="true">ctrl k</kbd>
130130 </button>
131- <button
132- type="button"
133- class="btn icon"
134- data-install
135- hidden
136- aria-label="Install app"
137- title="install ermite as an app"
138- >
139- <span class="ico i-download" aria-hidden="true"></span>
140- </button>
141131 if SignedIn(ctx) {
142132 <form class="owner" method="post" action="/-/logout">
143133 <span class="owner-badge" title="signed in as the owner"><span class="ico i-user" aria-hidden="true"></span>{ l.Owner }</span>
@@ -128,16 +128,6 @@ templ Header(l Layout) {
128 <span class="search-label">Go to…</span>128 <span class="search-label">Go to…</span>
129 <kbd aria-hidden="true">ctrl k</kbd>129 <kbd aria-hidden="true">ctrl k</kbd>
130 </button>130 </button>
131- <button
132- type="button"
133- class="btn icon"
134- data-install
135- hidden
136- aria-label="Install app"
137- title="install ermite as an app"
138- >
139- <span class="ico i-download" aria-hidden="true"></span>
140- </button>
141 if SignedIn(ctx) {131 if SignedIn(ctx) {
142 <form class="owner" method="post" action="/-/logout">132 <form class="owner" method="post" action="/-/logout">
143 <span class="owner-badge" title="signed in as the owner"><span class="ico i-user" aria-hidden="true"></span>{ l.Owner }</span>133 <span class="owner-badge" title="signed in as the owner"><span class="ico i-user" aria-hidden="true"></span>{ l.Owner }</span>
M internal/adapters/web/shell_templ.go +11 -11 generated file: collapsed
M internal/adapters/web/static/css/20-shell.css +5 -9
@@ -26,7 +26,6 @@
2626 .i-key { --i: url(icons/key.svg); }
2727 .i-log-in { --i: url(icons/log-in.svg); }
2828 .i-log-out { --i: url(icons/log-out.svg); }
29- .i-download { --i: url(icons/download.svg); }
3029
3130 .pill {
3231 display: inline-flex; align-items: center; height: 20px; padding: 0 var(--s2); margin-left: var(--s1);
@@ -135,15 +134,12 @@
135134 .rail section > .list { margin: var(--s2) 0; }
136135 .rail section > p + p { margin-top: calc(-1 * var(--s2)); }
137136
138- /* newest tag, a row of About in the summary rail */
137+ /* newest tag, a row of About in the summary rail: one line of text like
138+ the rows above it ("v0.9.0 · 36h ago · 9 tags") */
139139 .kv dd.latest-tag {
140- display: flex; align-items: baseline; gap: var(--s2);
141- & .tag-name { display: inline-flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; font-weight: 700; font-family: var(--mono); color: var(--ink-hi); text-decoration: none; }
142- & .tag-name .ico { flex: none; }
143- & .tag-name:hover { text-decoration: underline; }
144- & time { white-space: nowrap; }
145- & .all-tags { margin-left: auto; white-space: nowrap; color: var(--ink-dim); text-decoration: none; }
146- & .all-tags:hover { color: var(--ink-hi); text-decoration: underline; }
140+ & a { color: inherit; text-decoration: none; }
141+ & .tag-name { font-weight: 700; color: var(--ink-hi); }
142+ & a:hover { color: var(--ink-hi); text-decoration: underline; }
147143 }
148144
149145 .kv {
@@ -26,7 +26,6 @@
26 .i-key { --i: url(icons/key.svg); }26 .i-key { --i: url(icons/key.svg); }
27 .i-log-in { --i: url(icons/log-in.svg); }27 .i-log-in { --i: url(icons/log-in.svg); }
28 .i-log-out { --i: url(icons/log-out.svg); }28 .i-log-out { --i: url(icons/log-out.svg); }
29- .i-download { --i: url(icons/download.svg); }
30 29
31 .pill {30 .pill {
32 display: inline-flex; align-items: center; height: 20px; padding: 0 var(--s2); margin-left: var(--s1);31 display: inline-flex; align-items: center; height: 20px; padding: 0 var(--s2); margin-left: var(--s1);
@@ -135,15 +134,12 @@
135 .rail section > .list { margin: var(--s2) 0; }134 .rail section > .list { margin: var(--s2) 0; }
136 .rail section > p + p { margin-top: calc(-1 * var(--s2)); }135 .rail section > p + p { margin-top: calc(-1 * var(--s2)); }
137 136
138- /* newest tag, a row of About in the summary rail */137+ /* newest tag, a row of About in the summary rail: one line of text like
138+ the rows above it ("v0.9.0 · 36h ago · 9 tags") */
139 .kv dd.latest-tag {139 .kv dd.latest-tag {
140- display: flex; align-items: baseline; gap: var(--s2);140+ & a { color: inherit; text-decoration: none; }
141- & .tag-name { display: inline-flex; align-items: center; gap: 4px; min-width: 0; overflow: hidden; font-weight: 700; font-family: var(--mono); color: var(--ink-hi); text-decoration: none; }141+ & .tag-name { font-weight: 700; color: var(--ink-hi); }
142- & .tag-name .ico { flex: none; }142+ & a:hover { color: var(--ink-hi); text-decoration: underline; }
143- & .tag-name:hover { text-decoration: underline; }
144- & time { white-space: nowrap; }
145- & .all-tags { margin-left: auto; white-space: nowrap; color: var(--ink-dim); text-decoration: none; }
146- & .all-tags:hover { color: var(--ink-hi); text-decoration: underline; }
147 }143 }
148 144
149 .kv {145 .kv {
M internal/adapters/web/static/css/60-mr.css +6 -2
@@ -6,8 +6,12 @@
66 .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); }
77 .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); }
88 .mr-state[data-state="draft"]::before { content: "◌ "; color: var(--ink-dim); }
9- .mr-desc { max-width: var(--measure); white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); }
10- .mr-desc a, .c-text a { text-decoration: underline; }
9+ /* the description: Markdown as a README, as wide as the header card above;
10+ the text inside keeps the reading measure */
11+ .mr-desc.prose { max-width: none; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); }
12+ .mr-desc.prose > * { max-width: var(--measure); }
13+ .mr-desc.prose > pre, .mr-desc.prose > table { max-width: 100%; }
14+ .c-text a { text-decoration: underline; }
1115
1216 .since {
1317 display: inline-flex; flex-wrap: wrap; gap: var(--s1); padding: var(--s1);
@@ -6,8 +6,12 @@
6 .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); }6 .mr-state[data-state="merged"]::before { content: "✓ "; color: var(--teal); }
7 .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); }7 .mr-state[data-state="closed"]::before { content: "✗ "; color: var(--red); }
8 .mr-state[data-state="draft"]::before { content: "◌ "; color: var(--ink-dim); }8 .mr-state[data-state="draft"]::before { content: "◌ "; color: var(--ink-dim); }
9- .mr-desc { max-width: var(--measure); white-space: pre-wrap; overflow-wrap: anywhere; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); }9+ /* the description: Markdown as a README, as wide as the header card above;
10- .mr-desc a, .c-text a { text-decoration: underline; }10+ the text inside keeps the reading measure */
11+ .mr-desc.prose { max-width: none; padding: var(--s4) var(--s5); background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); }
12+ .mr-desc.prose > * { max-width: var(--measure); }
13+ .mr-desc.prose > pre, .mr-desc.prose > table { max-width: 100%; }
14+ .c-text a { text-decoration: underline; }
11 15
12 .since {16 .since {
13 display: inline-flex; flex-wrap: wrap; gap: var(--s1); padding: var(--s1);17 display: inline-flex; flex-wrap: wrap; gap: var(--s1); padding: var(--s1);
D internal/adapters/web/static/icons/download.svg +0 -1
@@ -1,1 +0,0 @@
1-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
@@ -1,1 +0,0 @@
1-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg>
M internal/adapters/web/static/js/ermite.js +6 -51
@@ -11,7 +11,7 @@
1111 // 4. The file of the diff in view is marked in the table of contents.
1212 // 5. A handler that fails is shown in the hint badge.
1313 // 6. The lines a diff hides load in place (ermiteGaps; e and E).
14-// 7. The service worker and the "install app" button (ermiteInstall).
14+// 7. The service worker (ermiteInstall).
1515 (() => {
1616 // Marks the page as scripted: what does nothing without scripts (the filters,
1717 // the "go to" and help buttons) is hidden until this class is set (see the
@@ -366,20 +366,12 @@
366366 };
367367 })();
368368
369- // The installed app. The service worker (/-/sw.js) is registered once the page
370- // has loaded. Chrome offers the install by itself only as an icon in the
371- // address bar of a desktop (or a mini-bar on a phone): the page keeps the
372- // beforeinstallprompt event and shows its own button ([data-install] in the
373- // header), which asks the browser to prompt. The button stays hidden when the
374- // app is already running installed or once it was installed. Self-contained:
375- // jstest/install.test.mjs evaluates this block alone.
369+ // The installed app: the service worker (/-/sw.js) is registered once the
370+ // page has loaded. With it and the manifest the browser offers the install
371+ // by itself (the address bar of a desktop, the menu or a banner on a phone);
372+ // the page leaves that offer alone. Self-contained: jstest/install.test.mjs
373+ // evaluates this block alone.
376374 window.ermiteInstall = (() => {
377- let deferred = null;
378- const buttons = () => $$('[data-install]');
379- const show = (on) => { for (const b of buttons()) b.hidden = !on; };
380- const installed = () =>
381- !!window.matchMedia?.('(display-mode: standalone), (display-mode: minimal-ui), (display-mode: window-controls-overlay)').matches ||
382- navigator.standalone === true;
383375 const api = {
384376 // register installs the service worker; a browser without one, or a
385377 // refusal, leaves the site working as before.
@@ -387,44 +379,7 @@
387379 if (!navigator.serviceWorker) return Promise.resolve(null);
388380 return navigator.serviceWorker.register('/-/sw.js', { scope: '/' }).catch(() => null);
389381 },
390- // offer keeps the event the browser sent and shows the button.
391- offer(e) {
392- if (installed()) return;
393- e.preventDefault();
394- deferred = e;
395- show(true);
396- },
397- // prompt asks the browser to show its install dialog. The event works
398- // once: a new one comes if the visitor dismisses the dialog. The button
399- // stays while the dialog is up; once it hides, the focus it held moves
400- // to the search of the header rather than to the body.
401- async prompt() {
402- const e = deferred;
403- if (!e) return '';
404- deferred = null;
405- let outcome = '';
406- try {
407- await e.prompt();
408- outcome = (await e.userChoice).outcome;
409- } catch {
410- outcome = '';
411- }
412- const had = buttons().some((b) => b === document.activeElement);
413- show(false);
414- if (had) document.querySelector('[aria-controls="panel-cmd"]')?.focus?.();
415- return outcome;
416- },
417- done() {
418- deferred = null;
419- show(false);
420- },
421382 };
422- addEventListener('beforeinstallprompt', api.offer);
423- addEventListener('appinstalled', api.done);
424- addEventListener('click', (e) => {
425- // pending is the prompt the last click started (its outcome)
426- if (e.target.closest?.('[data-install]')) api.pending = api.prompt();
427- });
428383 if (document.readyState === 'complete') api.register();
429384 else addEventListener('load', () => api.register(), { once: true });
430385 return api;
@@ -11,7 +11,7 @@
11 // 4. The file of the diff in view is marked in the table of contents.11 // 4. The file of the diff in view is marked in the table of contents.
12 // 5. A handler that fails is shown in the hint badge.12 // 5. A handler that fails is shown in the hint badge.
13 // 6. The lines a diff hides load in place (ermiteGaps; e and E).13 // 6. The lines a diff hides load in place (ermiteGaps; e and E).
14-// 7. The service worker and the "install app" button (ermiteInstall).14+// 7. The service worker (ermiteInstall).
15 (() => {15 (() => {
16 // Marks the page as scripted: what does nothing without scripts (the filters,16 // Marks the page as scripted: what does nothing without scripts (the filters,
17 // the "go to" and help buttons) is hidden until this class is set (see the17 // the "go to" and help buttons) is hidden until this class is set (see the
@@ -366,20 +366,12 @@
366 };366 };
367 })();367 })();
368 368
369- // The installed app. The service worker (/-/sw.js) is registered once the page369+ // The installed app: the service worker (/-/sw.js) is registered once the
370- // has loaded. Chrome offers the install by itself only as an icon in the370+ // page has loaded. With it and the manifest the browser offers the install
371- // address bar of a desktop (or a mini-bar on a phone): the page keeps the371+ // by itself (the address bar of a desktop, the menu or a banner on a phone);
372- // beforeinstallprompt event and shows its own button ([data-install] in the372+ // the page leaves that offer alone. Self-contained: jstest/install.test.mjs
373- // header), which asks the browser to prompt. The button stays hidden when the373+ // evaluates this block alone.
374- // app is already running installed or once it was installed. Self-contained:
375- // jstest/install.test.mjs evaluates this block alone.
376 window.ermiteInstall = (() => {374 window.ermiteInstall = (() => {
377- let deferred = null;
378- const buttons = () => $$('[data-install]');
379- const show = (on) => { for (const b of buttons()) b.hidden = !on; };
380- const installed = () =>
381- !!window.matchMedia?.('(display-mode: standalone), (display-mode: minimal-ui), (display-mode: window-controls-overlay)').matches ||
382- navigator.standalone === true;
383 const api = {375 const api = {
384 // register installs the service worker; a browser without one, or a376 // register installs the service worker; a browser without one, or a
385 // refusal, leaves the site working as before.377 // refusal, leaves the site working as before.
@@ -387,44 +379,7 @@
387 if (!navigator.serviceWorker) return Promise.resolve(null);379 if (!navigator.serviceWorker) return Promise.resolve(null);
388 return navigator.serviceWorker.register('/-/sw.js', { scope: '/' }).catch(() => null);380 return navigator.serviceWorker.register('/-/sw.js', { scope: '/' }).catch(() => null);
389 },381 },
390- // offer keeps the event the browser sent and shows the button.
391- offer(e) {
392- if (installed()) return;
393- e.preventDefault();
394- deferred = e;
395- show(true);
396- },
397- // prompt asks the browser to show its install dialog. The event works
398- // once: a new one comes if the visitor dismisses the dialog. The button
399- // stays while the dialog is up; once it hides, the focus it held moves
400- // to the search of the header rather than to the body.
401- async prompt() {
402- const e = deferred;
403- if (!e) return '';
404- deferred = null;
405- let outcome = '';
406- try {
407- await e.prompt();
408- outcome = (await e.userChoice).outcome;
409- } catch {
410- outcome = '';
411- }
412- const had = buttons().some((b) => b === document.activeElement);
413- show(false);
414- if (had) document.querySelector('[aria-controls="panel-cmd"]')?.focus?.();
415- return outcome;
416- },
417- done() {
418- deferred = null;
419- show(false);
420- },
421 };382 };
422- addEventListener('beforeinstallprompt', api.offer);
423- addEventListener('appinstalled', api.done);
424- addEventListener('click', (e) => {
425- // pending is the prompt the last click started (its outcome)
426- if (e.target.closest?.('[data-install]')) api.pending = api.prompt();
427- });
428 if (document.readyState === 'complete') api.register();383 if (document.readyState === 'complete') api.register();
429 else addEventListener('load', () => api.register(), { once: true });384 else addEventListener('load', () => api.register(), { once: true });
430 return api;385 return api;
M internal/app/testdata/web/blob-binary.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-code.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-empty.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-image.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-markdown.html +0 -4
@@ -69,10 +69,6 @@
6969 <span class="search-label">Go to…</span>
7070 <kbd aria-hidden="true">ctrl k</kbd>
7171 </button>
72-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
73-<span class="ico i-download" aria-hidden="true">
74-</span>
75-</button>
7672 </div>
7773 </div>
7874 <nav class="tabs" aria-label="Sections">
@@ -69,10 +69,6 @@
69 <span class="search-label">Go to…</span>69 <span class="search-label">Go to…</span>
70 <kbd aria-hidden="true">ctrl k</kbd>70 <kbd aria-hidden="true">ctrl k</kbd>
71 </button>71 </button>
72-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
73-<span class="ico i-download" aria-hidden="true">
74-</span>
75-</button>
76 </div>72 </div>
77 </div>73 </div>
78 <nav class="tabs" aria-label="Sections">74 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-svg.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-text.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-too-large.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/blob-trojan.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-big.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-binary.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-merge.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-modify-split.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-modify.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-rename.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-root.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commit-trojan.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commits-page2.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commits-path.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commits-root.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/commits-small.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/empty.html +0 -4
@@ -51,10 +51,6 @@
5151 <span class="search-label">Go to…</span>
5252 <kbd aria-hidden="true">ctrl k</kbd>
5353 </button>
54-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
55-<span class="ico i-download" aria-hidden="true">
56-</span>
57-</button>
5854 </div>
5955 </div>
6056 </header>
@@ -51,10 +51,6 @@
51 <span class="search-label">Go to…</span>51 <span class="search-label">Go to…</span>
52 <kbd aria-hidden="true">ctrl k</kbd>52 <kbd aria-hidden="true">ctrl k</kbd>
53 </button>53 </button>
54-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
55-<span class="ico i-download" aria-hidden="true">
56-</span>
57-</button>
58 </div>54 </div>
59 </div>55 </div>
60 </header>56 </header>
M internal/app/testdata/web/home.html +0 -4
@@ -44,10 +44,6 @@
4444 <span class="search-label">Go to…</span>
4545 <kbd aria-hidden="true">ctrl k</kbd>
4646 </button>
47-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
48-<span class="ico i-download" aria-hidden="true">
49-</span>
50-</button>
5147 </div>
5248 </div>
5349 </header>
@@ -44,10 +44,6 @@
44 <span class="search-label">Go to…</span>44 <span class="search-label">Go to…</span>
45 <kbd aria-hidden="true">ctrl k</kbd>45 <kbd aria-hidden="true">ctrl k</kbd>
46 </button>46 </button>
47-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
48-<span class="ico i-download" aria-hidden="true">
49-</span>
50-</button>
51 </div>47 </div>
52 </div>48 </div>
53 </header>49 </header>
M internal/app/testdata/web/not-found-page.html +0 -4
@@ -51,10 +51,6 @@
5151 <span class="search-label">Go to…</span>
5252 <kbd aria-hidden="true">ctrl k</kbd>
5353 </button>
54-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
55-<span class="ico i-download" aria-hidden="true">
56-</span>
57-</button>
5854 </div>
5955 </div>
6056 </header>
@@ -51,10 +51,6 @@
51 <span class="search-label">Go to…</span>51 <span class="search-label">Go to…</span>
52 <kbd aria-hidden="true">ctrl k</kbd>52 <kbd aria-hidden="true">ctrl k</kbd>
53 </button>53 </button>
54-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
55-<span class="ico i-download" aria-hidden="true">
56-</span>
57-</button>
58 </div>54 </div>
59 </div>55 </div>
60 </header>56 </header>
M internal/app/testdata/web/not-found.html +0 -4
@@ -51,10 +51,6 @@
5151 <span class="search-label">Go to…</span>
5252 <kbd aria-hidden="true">ctrl k</kbd>
5353 </button>
54-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
55-<span class="ico i-download" aria-hidden="true">
56-</span>
57-</button>
5854 </div>
5955 </div>
6056 </header>
@@ -51,10 +51,6 @@
51 <span class="search-label">Go to…</span>51 <span class="search-label">Go to…</span>
52 <kbd aria-hidden="true">ctrl k</kbd>52 <kbd aria-hidden="true">ctrl k</kbd>
53 </button>53 </button>
54-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
55-<span class="ico i-download" aria-hidden="true">
56-</span>
57-</button>
58 </div>54 </div>
59 </div>55 </div>
60 </header>56 </header>
M internal/app/testdata/web/refs.html +0 -4
@@ -60,10 +60,6 @@
6060 <span class="search-label">Go to…</span>
6161 <kbd aria-hidden="true">ctrl k</kbd>
6262 </button>
63-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
64-<span class="ico i-download" aria-hidden="true">
65-</span>
66-</button>
6763 </div>
6864 </div>
6965 <nav class="tabs" aria-label="Sections">
@@ -60,10 +60,6 @@
60 <span class="search-label">Go to…</span>60 <span class="search-label">Go to…</span>
61 <kbd aria-hidden="true">ctrl k</kbd>61 <kbd aria-hidden="true">ctrl k</kbd>
62 </button>62 </button>
63-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
64-<span class="ico i-download" aria-hidden="true">
65-</span>
66-</button>
67 </div>63 </div>
68 </div>64 </div>
69 <nav class="tabs" aria-label="Sections">65 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/summary.html +1 -9
@@ -57,10 +57,6 @@
5757 <span class="search-label">Go to…</span>
5858 <kbd aria-hidden="true">ctrl k</kbd>
5959 </button>
60-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
61-<span class="ico i-download" aria-hidden="true">
62-</span>
63-</button>
6460 </div>
6561 </div>
6662 <nav class="tabs" aria-label="Sections">
@@ -153,11 +149,7 @@
153149 </dd>
154150 <dt>latest tag</dt>
155151 <dd class="latest-tag">
156-<a class="tag-name" href="/demo/tree/v0.2.0" title="add main.go">
157-<span class="ico i-tag" aria-hidden="true">
158-</span>v0.2.0</a>
159-<time class="dim" datetime="STAMP" title="STAMP">AGO</time>
160-<a class="all-tags" href="/demo/refs">2 tags</a>
152+<a class="tag-name" href="/demo/tree/v0.2.0" title="add main.go">v0.2.0</a> · <time datetime="STAMP" title="STAMP">AGO</time> · <a class="all-tags" href="/demo/refs">2 tags</a>
161153 </dd>
162154 </dl>
163155 </section>
@@ -57,10 +57,6 @@
57 <span class="search-label">Go to…</span>57 <span class="search-label">Go to…</span>
58 <kbd aria-hidden="true">ctrl k</kbd>58 <kbd aria-hidden="true">ctrl k</kbd>
59 </button>59 </button>
60-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
61-<span class="ico i-download" aria-hidden="true">
62-</span>
63-</button>
64 </div>60 </div>
65 </div>61 </div>
66 <nav class="tabs" aria-label="Sections">62 <nav class="tabs" aria-label="Sections">
@@ -153,11 +149,7 @@
153 </dd>149 </dd>
154 <dt>latest tag</dt>150 <dt>latest tag</dt>
155 <dd class="latest-tag">151 <dd class="latest-tag">
156-<a class="tag-name" href="/demo/tree/v0.2.0" title="add main.go">152+<a class="tag-name" href="/demo/tree/v0.2.0" title="add main.go">v0.2.0</a> · <time datetime="STAMP" title="STAMP">AGO</time> · <a class="all-tags" href="/demo/refs">2 tags</a>
157-<span class="ico i-tag" aria-hidden="true">
158-</span>v0.2.0</a>
159-<time class="dim" datetime="STAMP" title="STAMP">AGO</time>
160-<a class="all-tags" href="/demo/refs">2 tags</a>
161 </dd>153 </dd>
162 </dl>154 </dl>
163 </section>155 </section>
M internal/app/testdata/web/tree-nested.html +0 -4
@@ -69,10 +69,6 @@
6969 <span class="search-label">Go to…</span>
7070 <kbd aria-hidden="true">ctrl k</kbd>
7171 </button>
72-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
73-<span class="ico i-download" aria-hidden="true">
74-</span>
75-</button>
7672 </div>
7773 </div>
7874 <nav class="tabs" aria-label="Sections">
@@ -69,10 +69,6 @@
69 <span class="search-label">Go to…</span>69 <span class="search-label">Go to…</span>
70 <kbd aria-hidden="true">ctrl k</kbd>70 <kbd aria-hidden="true">ctrl k</kbd>
71 </button>71 </button>
72-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
73-<span class="ico i-download" aria-hidden="true">
74-</span>
75-</button>
76 </div>72 </div>
77 </div>73 </div>
78 <nav class="tabs" aria-label="Sections">74 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/tree-root.html +0 -4
@@ -63,10 +63,6 @@
6363 <span class="search-label">Go to…</span>
6464 <kbd aria-hidden="true">ctrl k</kbd>
6565 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
7066 </div>
7167 </div>
7268 <nav class="tabs" aria-label="Sections">
@@ -63,10 +63,6 @@
63 <span class="search-label">Go to…</span>63 <span class="search-label">Go to…</span>
64 <kbd aria-hidden="true">ctrl k</kbd>64 <kbd aria-hidden="true">ctrl k</kbd>
65 </button>65 </button>
66-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
67-<span class="ico i-download" aria-hidden="true">
68-</span>
69-</button>
70 </div>66 </div>
71 </div>67 </div>
72 <nav class="tabs" aria-label="Sections">68 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/tree-slash-ref.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/testdata/web/tree-subdir.html +0 -4
@@ -66,10 +66,6 @@
6666 <span class="search-label">Go to…</span>
6767 <kbd aria-hidden="true">ctrl k</kbd>
6868 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
7369 </div>
7470 </div>
7571 <nav class="tabs" aria-label="Sections">
@@ -66,10 +66,6 @@
66 <span class="search-label">Go to…</span>66 <span class="search-label">Go to…</span>
67 <kbd aria-hidden="true">ctrl k</kbd>67 <kbd aria-hidden="true">ctrl k</kbd>
68 </button>68 </button>
69-<button type="button" class="btn icon" data-install hidden aria-label="Install app" title="install ermite as an app">
70-<span class="ico i-download" aria-hidden="true">
71-</span>
72-</button>
73 </div>69 </div>
74 </div>70 </div>
75 <nav class="tabs" aria-label="Sections">71 <nav class="tabs" aria-label="Sections">
M internal/app/web_mr_test.go +9 -3
@@ -98,7 +98,7 @@ func TestWebMergeRequestPagesRender(t *testing.T) {
9898 // Open one from the compare page.
9999 res := b.form("/proj/merge-requests", token, url.Values{
100100 "source": {"feat/search"}, "target": {"main"}, "title": {"Add <b>search</b>"},
101- "description": {"Finds things.\r\nSee https://example.org/spec) and javascript:alert(1)"},
101+ "description": {"## Summary\r\n\r\nFinds **things** with `Find()`.\r\n\r\n- See https://example.org/spec) and javascript:alert(1)\r\n- [x](javascript:alert(1)) <script>alert(1)</script>"},
102102 })
103103 require.Equal(t, http.StatusSeeOther, res.status, res.body)
104104 assert.Equal(t, "/proj/merge-requests/1", res.hdr.Get("Location"))
@@ -137,9 +137,15 @@ func TestWebMergeRequestPagesRender(t *testing.T) {
137137 assert.Contains(t, mr.body, "package search</td>")
138138 assert.Contains(t, mr.body, "no checks")
139139 assert.Contains(t, mr.body, "fast-forward, squash")
140- assert.Contains(t, mr.body, `<a href="https://example.org/spec" rel="noopener nofollow ugc">https://example.org/spec</a>)`, "the address is linked, not its closing bracket")
140+ // The description is Markdown, rendered as a README: no raw HTML, no
141+ // script URL; its heading ids keep apart from the ids of the page.
142+ assert.Contains(t, mr.body, `<article class="mr-desc prose" aria-label="description" hx-ignore>`)
143+ assert.Contains(t, mr.body, `<h2 id="mr-desc-summary">Summary</h2>`)
144+ assert.Contains(t, mr.body, `<strong>things</strong>`)
145+ assert.Contains(t, mr.body, `<code>Find()</code>`)
146+ assert.Contains(t, mr.body, `<a href="https://example.org/spec"`, "a bare address is linked")
141147 assert.NotContains(t, mr.body, `href="javascript:`)
142- assert.Contains(t, mr.body, "Finds things.\nSee ", "line breaks are kept")
148+ assert.NotContains(t, mr.body, `<script>alert(1)</script>`)
143149 assert.NotContains(t, mr.body, "<form", "an anonymous visitor gets no form")
144150 assert.NotContains(t, mr.body, "ermite mr merge", "nor the merge line")
145151 assert.NotContains(t, mr.body, `id="merge-menu"`)
@@ -98,7 +98,7 @@ func TestWebMergeRequestPagesRender(t *testing.T) {
98 // Open one from the compare page.98 // Open one from the compare page.
99 res := b.form("/proj/merge-requests", token, url.Values{99 res := b.form("/proj/merge-requests", token, url.Values{
100 "source": {"feat/search"}, "target": {"main"}, "title": {"Add <b>search</b>"},100 "source": {"feat/search"}, "target": {"main"}, "title": {"Add <b>search</b>"},
101- "description": {"Finds things.\r\nSee https://example.org/spec) and javascript:alert(1)"},101+ "description": {"## Summary\r\n\r\nFinds **things** with `Find()`.\r\n\r\n- See https://example.org/spec) and javascript:alert(1)\r\n- [x](javascript:alert(1)) <script>alert(1)</script>"},
102 })102 })
103 require.Equal(t, http.StatusSeeOther, res.status, res.body)103 require.Equal(t, http.StatusSeeOther, res.status, res.body)
104 assert.Equal(t, "/proj/merge-requests/1", res.hdr.Get("Location"))104 assert.Equal(t, "/proj/merge-requests/1", res.hdr.Get("Location"))
@@ -137,9 +137,15 @@ func TestWebMergeRequestPagesRender(t *testing.T) {
137 assert.Contains(t, mr.body, "package search</td>")137 assert.Contains(t, mr.body, "package search</td>")
138 assert.Contains(t, mr.body, "no checks")138 assert.Contains(t, mr.body, "no checks")
139 assert.Contains(t, mr.body, "fast-forward, squash")139 assert.Contains(t, mr.body, "fast-forward, squash")
140- assert.Contains(t, mr.body, `<a href="https://example.org/spec" rel="noopener nofollow ugc">https://example.org/spec</a>)`, "the address is linked, not its closing bracket")140+ // The description is Markdown, rendered as a README: no raw HTML, no
141+ // script URL; its heading ids keep apart from the ids of the page.
142+ assert.Contains(t, mr.body, `<article class="mr-desc prose" aria-label="description" hx-ignore>`)
143+ assert.Contains(t, mr.body, `<h2 id="mr-desc-summary">Summary</h2>`)
144+ assert.Contains(t, mr.body, `<strong>things</strong>`)
145+ assert.Contains(t, mr.body, `<code>Find()</code>`)
146+ assert.Contains(t, mr.body, `<a href="https://example.org/spec"`, "a bare address is linked")
141 assert.NotContains(t, mr.body, `href="javascript:`)147 assert.NotContains(t, mr.body, `href="javascript:`)
142- assert.Contains(t, mr.body, "Finds things.\nSee ", "line breaks are kept")148+ assert.NotContains(t, mr.body, `<script>alert(1)</script>`)
143 assert.NotContains(t, mr.body, "<form", "an anonymous visitor gets no form")149 assert.NotContains(t, mr.body, "<form", "an anonymous visitor gets no form")
144 assert.NotContains(t, mr.body, "ermite mr merge", "nor the merge line")150 assert.NotContains(t, mr.body, "ermite mr merge", "nor the merge line")
145 assert.NotContains(t, mr.body, `id="merge-menu"`)151 assert.NotContains(t, mr.body, `id="merge-menu"`)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 43

↑↓ moveenter openesc close