fix(web): drop sequence aria-keyshortcuts and use fake timers in keyboard tests
- commit
- 4e3b6b376a37926f4cf831499d2d1eff24b7b9ab
- author
- brice · · 2026-10-22 07:58 UTC
- parent
- a1f6bdf
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +87 -97
M internal/adapters/web/jstest/keyboard.test.mjs +38 -4
| @@ -4,17 +4,25 @@ import { readFileSync } from 'node:fs'; | |||
| 4 | 4 | import { test } from 'node:test'; | |
| 5 | 5 | import { runInNewContext } from 'node:vm'; | |
| 6 | 6 | const source = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | |
| 7 | - | function keyboard({ off = false, typing = false, dialog = false } = {}) { | |
| 7 | + | function keyboard({ owner = true, off = false, typing = false, dialog = false } = {}) { | |
| 8 | 8 | let handler; | |
| 9 | 9 | const fired = []; | |
| 10 | + | const timers = new Map(); | |
| 11 | + | let timerID = 0; | |
| 10 | 12 | runInNewContext(source.slice(source.indexOf(' const single ='), source.indexOf(' // 2. A [data-fire]')), { | |
| 11 | 13 | addEventListener: (_, fn) => { handler = fn; }, | |
| 12 | 14 | window: { ermiteKeys: { blocks: () => off } }, | |
| 13 | 15 | ermite: { fire: name => fired.push(name) }, | |
| 14 | - | $: selector => selector === '[data-inbox]' || (dialog && selector === 'dialog[open]'), | |
| 15 | - | closeMenus: () => false, clearTimeout() {}, setTimeout() {}, | |
| 16 | + | $: selector => (owner && selector === '[data-inbox]') || (dialog && selector === 'dialog[open]'), | |
| 17 | + | closeMenus: () => false, | |
| 18 | + | clearTimeout: id => timers.delete(id), | |
| 19 | + | setTimeout: fn => { timers.set(++timerID, fn); return timerID; }, | |
| 16 | 20 | }); | |
| 17 | - | return { fired, press(key, shiftKey = false) { | |
| 21 | + | return { fired, expireTimers() { | |
| 22 | + | const callbacks = [...timers.values()]; | |
| 23 | + | timers.clear(); | |
| 24 | + | for (const callback of callbacks) callback(); | |
| 25 | + | }, press(key, shiftKey = false) { | |
| 18 | 26 | handler({ key, shiftKey, target: { closest: selector => typing && selector.startsWith('input'), blur() {} }, preventDefault() {} }); | |
| 19 | 27 | } }; | |
| 20 | 28 | } | |
| @@ -40,3 +48,29 @@ test('uppercase actions respect preference, fields and dialogs', () => { | |||
| 40 | 48 | assert.deepEqual(k.fired, []); | |
| 41 | 49 | } | |
| 42 | 50 | }); | |
| 51 | + | ||
| 52 | + | test('Shift+I opens inbox while issues need the g i sequence', () => { | |
| 53 | + | const k = keyboard(); | |
| 54 | + | k.press('I', true); k.press('i'); | |
| 55 | + | assert.deepEqual(k.fired, ['inbox']); | |
| 56 | + | k.press('g'); k.press('i'); | |
| 57 | + | assert.deepEqual(k.fired, ['inbox', 'i']); | |
| 58 | + | }); | |
| 59 | + | test('inbox shortcut respects preference, fields, dialogs, and owner target', () => { | |
| 60 | + | for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) { | |
| 61 | + | const k = keyboard(options); k.press('I', true); | |
| 62 | + | assert.deepEqual(k.fired, [], JSON.stringify(options)); | |
| 63 | + | } | |
| 64 | + | }); | |
| 65 | + | test('shifted punctuation still opens help and the command palette', () => { | |
| 66 | + | const k = keyboard(); | |
| 67 | + | k.press('?', true); k.press(':', true); | |
| 68 | + | assert.deepEqual(k.fired, ['help', 'cmd']); | |
| 69 | + | }); | |
| 70 | + | test('g prefix expires without scheduling real timers', () => { | |
| 71 | + | const k = keyboard(); | |
| 72 | + | k.press('g'); k.expireTimers(); k.press('s'); | |
| 73 | + | assert.deepEqual(k.fired, []); | |
| 74 | + | k.press('g'); k.press('s'); | |
| 75 | + | assert.deepEqual(k.fired, ['s']); | |
| 76 | + | }); | |
| @@ -4,17 +4,25 @@ import { readFileSync } from 'node:fs'; | |||||
| 4 | import { test } from 'node:test'; | 4 | import { test } from 'node:test'; | ||
| 5 | import { runInNewContext } from 'node:vm'; | 5 | import { runInNewContext } from 'node:vm'; | ||
| 6 | const source = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | 6 | const source = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | ||
| 7 | - | function keyboard({ off = false, typing = false, dialog = false } = {}) { | 7 | + | function keyboard({ owner = true, off = false, typing = false, dialog = false } = {}) { |
| 8 | let handler; | 8 | let handler; | ||
| 9 | const fired = []; | 9 | const fired = []; | ||
| 10 | + | const timers = new Map(); | |||
| 11 | + | let timerID = 0; | |||
| 10 | runInNewContext(source.slice(source.indexOf(' const single ='), source.indexOf(' // 2. A [data-fire]')), { | 12 | runInNewContext(source.slice(source.indexOf(' const single ='), source.indexOf(' // 2. A [data-fire]')), { | ||
| 11 | addEventListener: (_, fn) => { handler = fn; }, | 13 | addEventListener: (_, fn) => { handler = fn; }, | ||
| 12 | window: { ermiteKeys: { blocks: () => off } }, | 14 | window: { ermiteKeys: { blocks: () => off } }, | ||
| 13 | ermite: { fire: name => fired.push(name) }, | 15 | ermite: { fire: name => fired.push(name) }, | ||
| 14 | - | $: selector => selector === '[data-inbox]' || (dialog && selector === 'dialog[open]'), | 16 | + | $: selector => (owner && selector === '[data-inbox]') || (dialog && selector === 'dialog[open]'), |
| 15 | - | closeMenus: () => false, clearTimeout() {}, setTimeout() {}, | 17 | + | closeMenus: () => false, |
| 18 | + | clearTimeout: id => timers.delete(id), | |||
| 19 | + | setTimeout: fn => { timers.set(++timerID, fn); return timerID; }, | |||
| 16 | }); | 20 | }); | ||
| 17 | - | return { fired, press(key, shiftKey = false) { | 21 | + | return { fired, expireTimers() { |
| 22 | + | const callbacks = [...timers.values()]; | |||
| 23 | + | timers.clear(); | |||
| 24 | + | for (const callback of callbacks) callback(); | |||
| 25 | + | }, press(key, shiftKey = false) { | |||
| 18 | handler({ key, shiftKey, target: { closest: selector => typing && selector.startsWith('input'), blur() {} }, preventDefault() {} }); | 26 | handler({ key, shiftKey, target: { closest: selector => typing && selector.startsWith('input'), blur() {} }, preventDefault() {} }); | ||
| 19 | } }; | 27 | } }; | ||
| 20 | } | 28 | } | ||
| @@ -40,3 +48,29 @@ test('uppercase actions respect preference, fields and dialogs', () => { | |||||
| 40 | assert.deepEqual(k.fired, []); | 48 | assert.deepEqual(k.fired, []); | ||
| 41 | } | 49 | } | ||
| 42 | }); | 50 | }); | ||
| 51 | + | ||||
| 52 | + | test('Shift+I opens inbox while issues need the g i sequence', () => { | |||
| 53 | + | const k = keyboard(); | |||
| 54 | + | k.press('I', true); k.press('i'); | |||
| 55 | + | assert.deepEqual(k.fired, ['inbox']); | |||
| 56 | + | k.press('g'); k.press('i'); | |||
| 57 | + | assert.deepEqual(k.fired, ['inbox', 'i']); | |||
| 58 | + | }); | |||
| 59 | + | test('inbox shortcut respects preference, fields, dialogs, and owner target', () => { | |||
| 60 | + | for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) { | |||
| 61 | + | const k = keyboard(options); k.press('I', true); | |||
| 62 | + | assert.deepEqual(k.fired, [], JSON.stringify(options)); | |||
| 63 | + | } | |||
| 64 | + | }); | |||
| 65 | + | test('shifted punctuation still opens help and the command palette', () => { | |||
| 66 | + | const k = keyboard(); | |||
| 67 | + | k.press('?', true); k.press(':', true); | |||
| 68 | + | assert.deepEqual(k.fired, ['help', 'cmd']); | |||
| 69 | + | }); | |||
| 70 | + | test('g prefix expires without scheduling real timers', () => { | |||
| 71 | + | const k = keyboard(); | |||
| 72 | + | k.press('g'); k.expireTimers(); k.press('s'); | |||
| 73 | + | assert.deepEqual(k.fired, []); | |||
| 74 | + | k.press('g'); k.press('s'); | |||
| 75 | + | assert.deepEqual(k.fired, ['s']); | |||
| 76 | + | }); | |||
D internal/adapters/web/jstest/keys.test.mjs +0 -31
| @@ -1,31 +0,0 @@ | |||
| 1 | - | // Exercise the production keydown handler without loading the rest of the page. | |
| 2 | - | import assert from 'node:assert/strict'; | |
| 3 | - | import { readFileSync } from 'node:fs'; | |
| 4 | - | import { test } from 'node:test'; | |
| 5 | - | const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | |
| 6 | - | const block = src.slice(src.indexOf(' // 1. Keys.'), src.indexOf(' // 2. A [data-fire]')); | |
| 7 | - | ||
| 8 | - | function page({ owner = true, off = false, typing = false, dialog = false } = {}) { | |
| 9 | - | let handler; | |
| 10 | - | const fired = []; | |
| 11 | - | const window = { ermiteKeys: { blocks: () => off } }; | |
| 12 | - | const $ = (s) => s === 'dialog[open]' ? dialog : s === '[data-inbox]' ? owner : null; | |
| 13 | - | new Function('window', '$', 'ermite', 'addEventListener', block)(window, $, { fire: (n) => fired.push(n) }, (_, f) => { handler = f; }); | |
| 14 | - | return (key) => { | |
| 15 | - | const e = { key, shiftKey: key === 'I', preventDefault() {}, target: { closest: (s) => s.startsWith('input') && typing } }; | |
| 16 | - | handler(e); | |
| 17 | - | return fired; | |
| 18 | - | }; | |
| 19 | - | } | |
| 20 | - | test('Shift+I opens inbox while issues need the g i sequence', () => { | |
| 21 | - | const press = page(); | |
| 22 | - | assert.deepEqual(press('I'), ['inbox']); | |
| 23 | - | assert.deepEqual(press('i'), ['inbox']); | |
| 24 | - | press('g'); | |
| 25 | - | assert.deepEqual(press('i'), ['inbox', 'i']); | |
| 26 | - | }); | |
| 27 | - | test('inbox shortcut respects preference, fields, dialogs, and owner target', () => { | |
| 28 | - | for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) { | |
| 29 | - | assert.deepEqual(page(options)('I'), [], JSON.stringify(options)); | |
| 30 | - | } | |
| 31 | - | }); | |
| @@ -1,31 +0,0 @@ | |||||
| 1 | - | // Exercise the production keydown handler without loading the rest of the page. | |||
| 2 | - | import assert from 'node:assert/strict'; | |||
| 3 | - | import { readFileSync } from 'node:fs'; | |||
| 4 | - | import { test } from 'node:test'; | |||
| 5 | - | const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | |||
| 6 | - | const block = src.slice(src.indexOf(' // 1. Keys.'), src.indexOf(' // 2. A [data-fire]')); | |||
| 7 | - | ||||
| 8 | - | function page({ owner = true, off = false, typing = false, dialog = false } = {}) { | |||
| 9 | - | let handler; | |||
| 10 | - | const fired = []; | |||
| 11 | - | const window = { ermiteKeys: { blocks: () => off } }; | |||
| 12 | - | const $ = (s) => s === 'dialog[open]' ? dialog : s === '[data-inbox]' ? owner : null; | |||
| 13 | - | new Function('window', '$', 'ermite', 'addEventListener', block)(window, $, { fire: (n) => fired.push(n) }, (_, f) => { handler = f; }); | |||
| 14 | - | return (key) => { | |||
| 15 | - | const e = { key, shiftKey: key === 'I', preventDefault() {}, target: { closest: (s) => s.startsWith('input') && typing } }; | |||
| 16 | - | handler(e); | |||
| 17 | - | return fired; | |||
| 18 | - | }; | |||
| 19 | - | } | |||
| 20 | - | test('Shift+I opens inbox while issues need the g i sequence', () => { | |||
| 21 | - | const press = page(); | |||
| 22 | - | assert.deepEqual(press('I'), ['inbox']); | |||
| 23 | - | assert.deepEqual(press('i'), ['inbox']); | |||
| 24 | - | press('g'); | |||
| 25 | - | assert.deepEqual(press('i'), ['inbox', 'i']); | |||
| 26 | - | }); | |||
| 27 | - | test('inbox shortcut respects preference, fields, dialogs, and owner target', () => { | |||
| 28 | - | for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) { | |||
| 29 | - | assert.deepEqual(page(options)('I'), [], JSON.stringify(options)); | |||
| 30 | - | } | |||
| 31 | - | }); | |||
M internal/adapters/web/keys_test.go +1 -1
| @@ -17,7 +17,7 @@ func TestSectionShortcutSequences(t *testing.T) { | |||
| 17 | 17 | bindings := renderToString(t, Keys()) | |
| 18 | 18 | for _, section := range sections { | |
| 19 | 19 | assert.Contains(t, header, `data-key="g `+section.Key+`"`) | |
| 20 | - | assert.Contains(t, header, `aria-keyshortcuts="g `+section.Key+`"`) | |
| 20 | + | assert.NotContains(t, header, `aria-keyshortcuts="g `+section.Key+`"`) | |
| 21 | 21 | assert.Contains(t, header, `<kbd aria-hidden="true">g `+section.Key+`</kbd>`) | |
| 22 | 22 | assert.Contains(t, bindings, "ermite.tab('"+section.Key+"')") | |
| 23 | 23 | } | |
| @@ -17,7 +17,7 @@ func TestSectionShortcutSequences(t *testing.T) { | |||||
| 17 | bindings := renderToString(t, Keys()) | 17 | bindings := renderToString(t, Keys()) | ||
| 18 | for _, section := range sections { | 18 | for _, section := range sections { | ||
| 19 | assert.Contains(t, header, `data-key="g `+section.Key+`"`) | 19 | assert.Contains(t, header, `data-key="g `+section.Key+`"`) | ||
| 20 | - | assert.Contains(t, header, `aria-keyshortcuts="g `+section.Key+`"`) | 20 | + | assert.NotContains(t, header, `aria-keyshortcuts="g `+section.Key+`"`) |
| 21 | assert.Contains(t, header, `<kbd aria-hidden="true">g `+section.Key+`</kbd>`) | 21 | assert.Contains(t, header, `<kbd aria-hidden="true">g `+section.Key+`</kbd>`) | ||
| 22 | assert.Contains(t, bindings, "ermite.tab('"+section.Key+"')") | 22 | assert.Contains(t, bindings, "ermite.tab('"+section.Key+"')") | ||
| 23 | } | 23 | } | ||
M internal/adapters/web/releases_test.go +2 -1
| @@ -120,7 +120,8 @@ func TestReleasePages(t *testing.T) { | |||
| 120 | 120 | assert.Contains(t, body, "2026-10-12") | |
| 121 | 121 | assert.Contains(t, body, `<td>1</td>`) | |
| 122 | 122 | assert.Contains(t, body, `?page=next%2B%2F%3D`) | |
| 123 | - | assert.Contains(t, body, `aria-keyshortcuts="g r"`) | |
| 123 | + | assert.Contains(t, body, `data-key="g r"`) | |
| 124 | + | assert.NotContains(t, body, `aria-keyshortcuts="g r"`) | |
| 124 | 125 | assert.Contains(t, body, "view=releases") | |
| 125 | 126 | } | |
| 126 | 127 | owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "") | |
| @@ -120,7 +120,8 @@ func TestReleasePages(t *testing.T) { | |||||
| 120 | assert.Contains(t, body, "2026-10-12") | 120 | assert.Contains(t, body, "2026-10-12") | ||
| 121 | assert.Contains(t, body, `<td>1</td>`) | 121 | assert.Contains(t, body, `<td>1</td>`) | ||
| 122 | assert.Contains(t, body, `?page=next%2B%2F%3D`) | 122 | assert.Contains(t, body, `?page=next%2B%2F%3D`) | ||
| 123 | - | assert.Contains(t, body, `aria-keyshortcuts="g r"`) | 123 | + | assert.Contains(t, body, `data-key="g r"`) |
| 124 | + | assert.NotContains(t, body, `aria-keyshortcuts="g r"`) | |||
| 124 | assert.Contains(t, body, "view=releases") | 125 | assert.Contains(t, body, "view=releases") | ||
| 125 | } | 126 | } | ||
| 126 | owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "") | 127 | owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "") | ||
M internal/adapters/web/shell.templ +0 -1
| @@ -173,7 +173,6 @@ templ Header(l Layout) { | |||
| 173 | 173 | <a | |
| 174 | 174 | href={ templ.SafeURL(s.Href) } | |
| 175 | 175 | data-key={ "g " + s.Key } | |
| 176 | - | aria-keyshortcuts={ "g " + s.Key } | |
| 177 | 176 | if s.Current { | |
| 178 | 177 | aria-current="page" | |
| 179 | 178 | } | |
| @@ -173,7 +173,6 @@ templ Header(l Layout) { | |||||
| 173 | <a | 173 | <a | ||
| 174 | href={ templ.SafeURL(s.Href) } | 174 | href={ templ.SafeURL(s.Href) } | ||
| 175 | data-key={ "g " + s.Key } | 175 | data-key={ "g " + s.Key } | ||
| 176 | - | aria-keyshortcuts={ "g " + s.Key } | |||
| 177 | if s.Current { | 176 | if s.Current { | ||
| 178 | aria-current="page" | 177 | aria-current="page" | ||
| 179 | } | 178 | } | ||
M internal/adapters/web/shell_templ.go +46 -59
| @@ -751,102 +751,89 @@ func Header(l Layout) templ.Component { | |||
| 751 | 751 | if templ_7745c5c3_Err != nil { | |
| 752 | 752 | return templ_7745c5c3_Err | |
| 753 | 753 | } | |
| 754 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" aria-keyshortcuts=\"") | |
| 755 | - | if templ_7745c5c3_Err != nil { | |
| 756 | - | return templ_7745c5c3_Err | |
| 757 | - | } | |
| 758 | - | var templ_7745c5c3_Var39 string | |
| 759 | - | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue("g " + s.Key) | |
| 760 | - | if templ_7745c5c3_Err != nil { | |
| 761 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 176, Col: 40} | |
| 762 | - | } | |
| 763 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39) | |
| 764 | - | if templ_7745c5c3_Err != nil { | |
| 765 | - | return templ_7745c5c3_Err | |
| 766 | - | } | |
| 767 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\"") | |
| 754 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\"") | |
| 768 | 755 | if templ_7745c5c3_Err != nil { | |
| 769 | 756 | return templ_7745c5c3_Err | |
| 770 | 757 | } | |
| 771 | 758 | if s.Current { | |
| 772 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, " aria-current=\"page\"") | |
| 759 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, " aria-current=\"page\"") | |
| 773 | 760 | if templ_7745c5c3_Err != nil { | |
| 774 | 761 | return templ_7745c5c3_Err | |
| 775 | 762 | } | |
| 776 | 763 | } | |
| 777 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, ">") | |
| 764 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, ">") | |
| 778 | 765 | if templ_7745c5c3_Err != nil { | |
| 779 | 766 | return templ_7745c5c3_Err | |
| 780 | 767 | } | |
| 781 | - | var templ_7745c5c3_Var40 string | |
| 782 | - | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(s.Label) | |
| 768 | + | var templ_7745c5c3_Var39 string | |
| 769 | + | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(s.Label) | |
| 783 | 770 | if templ_7745c5c3_Err != nil { | |
| 784 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 181, Col: 17} | |
| 771 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 180, Col: 17} | |
| 785 | 772 | } | |
| 786 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | |
| 773 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) | |
| 787 | 774 | if templ_7745c5c3_Err != nil { | |
| 788 | 775 | return templ_7745c5c3_Err | |
| 789 | 776 | } | |
| 790 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, " ") | |
| 777 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, " ") | |
| 791 | 778 | if templ_7745c5c3_Err != nil { | |
| 792 | 779 | return templ_7745c5c3_Err | |
| 793 | 780 | } | |
| 794 | 781 | if s.Count != "" { | |
| 795 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<span class=\"count\">") | |
| 782 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<span class=\"count\">") | |
| 796 | 783 | if templ_7745c5c3_Err != nil { | |
| 797 | 784 | return templ_7745c5c3_Err | |
| 798 | 785 | } | |
| 799 | - | var templ_7745c5c3_Var41 string | |
| 800 | - | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(s.Count) | |
| 786 | + | var templ_7745c5c3_Var40 string | |
| 787 | + | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(s.Count) | |
| 801 | 788 | if templ_7745c5c3_Err != nil { | |
| 802 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 183, Col: 38} | |
| 789 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 182, Col: 38} | |
| 803 | 790 | } | |
| 804 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | |
| 791 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | |
| 805 | 792 | if templ_7745c5c3_Err != nil { | |
| 806 | 793 | return templ_7745c5c3_Err | |
| 807 | 794 | } | |
| 808 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "<span class=\"sr\">") | |
| 795 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<span class=\"sr\">") | |
| 809 | 796 | if templ_7745c5c3_Err != nil { | |
| 810 | 797 | return templ_7745c5c3_Err | |
| 811 | 798 | } | |
| 812 | - | var templ_7745c5c3_Var42 string | |
| 813 | - | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(" open") | |
| 799 | + | var templ_7745c5c3_Var41 string | |
| 800 | + | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(" open") | |
| 814 | 801 | if templ_7745c5c3_Err != nil { | |
| 815 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 183, Col: 66} | |
| 802 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 182, Col: 66} | |
| 816 | 803 | } | |
| 817 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 804 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | |
| 818 | 805 | if templ_7745c5c3_Err != nil { | |
| 819 | 806 | return templ_7745c5c3_Err | |
| 820 | 807 | } | |
| 821 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</span></span> ") | |
| 808 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</span></span> ") | |
| 822 | 809 | if templ_7745c5c3_Err != nil { | |
| 823 | 810 | return templ_7745c5c3_Err | |
| 824 | 811 | } | |
| 825 | 812 | } | |
| 826 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<kbd aria-hidden=\"true\">") | |
| 813 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "<kbd aria-hidden=\"true\">") | |
| 827 | 814 | if templ_7745c5c3_Err != nil { | |
| 828 | 815 | return templ_7745c5c3_Err | |
| 829 | 816 | } | |
| 830 | - | var templ_7745c5c3_Var43 string | |
| 831 | - | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs("g " + s.Key) | |
| 817 | + | var templ_7745c5c3_Var42 string | |
| 818 | + | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs("g " + s.Key) | |
| 832 | 819 | if templ_7745c5c3_Err != nil { | |
| 833 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 185, Col: 46} | |
| 820 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 184, Col: 46} | |
| 834 | 821 | } | |
| 835 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | |
| 822 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | |
| 836 | 823 | if templ_7745c5c3_Err != nil { | |
| 837 | 824 | return templ_7745c5c3_Err | |
| 838 | 825 | } | |
| 839 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</kbd></a></li>") | |
| 826 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</kbd></a></li>") | |
| 840 | 827 | if templ_7745c5c3_Err != nil { | |
| 841 | 828 | return templ_7745c5c3_Err | |
| 842 | 829 | } | |
| 843 | 830 | } | |
| 844 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</ul></nav>") | |
| 831 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</ul></nav>") | |
| 845 | 832 | if templ_7745c5c3_Err != nil { | |
| 846 | 833 | return templ_7745c5c3_Err | |
| 847 | 834 | } | |
| 848 | 835 | } | |
| 849 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</header>") | |
| 836 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</header>") | |
| 850 | 837 | if templ_7745c5c3_Err != nil { | |
| 851 | 838 | return templ_7745c5c3_Err | |
| 852 | 839 | } | |
| @@ -872,38 +859,38 @@ func Footer(l Layout) templ.Component { | |||
| 872 | 859 | }() | |
| 873 | 860 | } | |
| 874 | 861 | ctx = templ.InitializeContext(ctx) | |
| 875 | - | templ_7745c5c3_Var44 := templ.GetChildren(ctx) | |
| 876 | - | if templ_7745c5c3_Var44 == nil { | |
| 877 | - | templ_7745c5c3_Var44 = templ.NopComponent | |
| 862 | + | templ_7745c5c3_Var43 := templ.GetChildren(ctx) | |
| 863 | + | if templ_7745c5c3_Var43 == nil { | |
| 864 | + | templ_7745c5c3_Var43 = templ.NopComponent | |
| 878 | 865 | } | |
| 879 | 866 | ctx = templ.ClearChildren(ctx) | |
| 880 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<footer class=\"foot\"><span>ermite ") | |
| 867 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<footer class=\"foot\"><span>ermite ") | |
| 881 | 868 | if templ_7745c5c3_Err != nil { | |
| 882 | 869 | return templ_7745c5c3_Err | |
| 883 | 870 | } | |
| 884 | - | var templ_7745c5c3_Var45 string | |
| 885 | - | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(l.Version) | |
| 871 | + | var templ_7745c5c3_Var44 string | |
| 872 | + | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(l.Version) | |
| 886 | 873 | if templ_7745c5c3_Err != nil { | |
| 887 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 199, Col: 26} | |
| 874 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 198, Col: 26} | |
| 888 | 875 | } | |
| 889 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 876 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) | |
| 890 | 877 | if templ_7745c5c3_Err != nil { | |
| 891 | 878 | return templ_7745c5c3_Err | |
| 892 | 879 | } | |
| 893 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</span> <a href=\"") | |
| 880 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</span> <a href=\"") | |
| 894 | 881 | if templ_7745c5c3_Err != nil { | |
| 895 | 882 | return templ_7745c5c3_Err | |
| 896 | 883 | } | |
| 897 | - | var templ_7745c5c3_Var46 templ.SafeURL | |
| 898 | - | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(l.SourceURL)) | |
| 884 | + | var templ_7745c5c3_Var45 templ.SafeURL | |
| 885 | + | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(l.SourceURL)) | |
| 899 | 886 | if templ_7745c5c3_Err != nil { | |
| 900 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 200, Col: 38} | |
| 887 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 199, Col: 38} | |
| 901 | 888 | } | |
| 902 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | |
| 889 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | |
| 903 | 890 | if templ_7745c5c3_Err != nil { | |
| 904 | 891 | return templ_7745c5c3_Err | |
| 905 | 892 | } | |
| 906 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\" rel=\"noopener\" title=\"source of this version (AGPL-3.0-or-later)\">source</a> <button type=\"button\" class=\"foot-keys\" data-js data-fire=\"help\" aria-haspopup=\"dialog\" aria-controls=\"panel-help\" aria-keyshortcuts=\"?\"><kbd aria-hidden=\"true\">?</kbd> keyboard shortcuts</button></footer>") | |
| 893 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "\" rel=\"noopener\" title=\"source of this version (AGPL-3.0-or-later)\">source</a> <button type=\"button\" class=\"foot-keys\" data-js data-fire=\"help\" aria-haspopup=\"dialog\" aria-controls=\"panel-help\" aria-keyshortcuts=\"?\"><kbd aria-hidden=\"true\">?</kbd> keyboard shortcuts</button></footer>") | |
| 907 | 894 | if templ_7745c5c3_Err != nil { | |
| 908 | 895 | return templ_7745c5c3_Err | |
| 909 | 896 | } | |
| @@ -931,12 +918,12 @@ func Hint(l Layout) templ.Component { | |||
| 931 | 918 | }() | |
| 932 | 919 | } | |
| 933 | 920 | ctx = templ.InitializeContext(ctx) | |
| 934 | - | templ_7745c5c3_Var47 := templ.GetChildren(ctx) | |
| 935 | - | if templ_7745c5c3_Var47 == nil { | |
| 936 | - | templ_7745c5c3_Var47 = templ.NopComponent | |
| 921 | + | templ_7745c5c3_Var46 := templ.GetChildren(ctx) | |
| 922 | + | if templ_7745c5c3_Var46 == nil { | |
| 923 | + | templ_7745c5c3_Var46 = templ.NopComponent | |
| 937 | 924 | } | |
| 938 | 925 | ctx = templ.ClearChildren(ctx) | |
| 939 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<div class=\"hint\" role=\"status\" aria-live=\"polite\"><span class=\"hint-nav\" :text=\"document.body.dataset.nav || ''\"></span> <span class=\"hint-sel\" :text=\"document.body.dataset.sel ? document.body.dataset.sel + ' · y permalink' : ''\"></span> <span class=\"hint-flash\" :text=\"document.body.dataset.flash || ''\"></span>") | |
| 926 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<div class=\"hint\" role=\"status\" aria-live=\"polite\"><span class=\"hint-nav\" :text=\"document.body.dataset.nav || ''\"></span> <span class=\"hint-sel\" :text=\"document.body.dataset.sel ? document.body.dataset.sel + ' · y permalink' : ''\"></span> <span class=\"hint-flash\" :text=\"document.body.dataset.flash || ''\"></span>") | |
| 940 | 927 | if templ_7745c5c3_Err != nil { | |
| 941 | 928 | return templ_7745c5c3_Err | |
| 942 | 929 | } | |
| @@ -944,7 +931,7 @@ func Hint(l Layout) templ.Component { | |||
| 944 | 931 | if templ_7745c5c3_Err != nil { | |
| 945 | 932 | return templ_7745c5c3_Err | |
| 946 | 933 | } | |
| 947 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "</div>") | |
| 934 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</div>") | |
| 948 | 935 | if templ_7745c5c3_Err != nil { | |
| 949 | 936 | return templ_7745c5c3_Err | |
| 950 | 937 | } | |
| @@ -751,102 +751,89 @@ func Header(l Layout) templ.Component { | |||||
| 751 | if templ_7745c5c3_Err != nil { | 751 | if templ_7745c5c3_Err != nil { | ||
| 752 | return templ_7745c5c3_Err | 752 | return templ_7745c5c3_Err | ||
| 753 | } | 753 | } | ||
| 754 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\" aria-keyshortcuts=\"") | 754 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 75, "\"") |
| 755 | - | if templ_7745c5c3_Err != nil { | |||
| 756 | - | return templ_7745c5c3_Err | |||
| 757 | - | } | |||
| 758 | - | var templ_7745c5c3_Var39 string | |||
| 759 | - | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.ResolveAttributeValue("g " + s.Key) | |||
| 760 | - | if templ_7745c5c3_Err != nil { | |||
| 761 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 176, Col: 40} | |||
| 762 | - | } | |||
| 763 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var39) | |||
| 764 | - | if templ_7745c5c3_Err != nil { | |||
| 765 | - | return templ_7745c5c3_Err | |||
| 766 | - | } | |||
| 767 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, "\"") | |||
| 768 | if templ_7745c5c3_Err != nil { | 755 | if templ_7745c5c3_Err != nil { | ||
| 769 | return templ_7745c5c3_Err | 756 | return templ_7745c5c3_Err | ||
| 770 | } | 757 | } | ||
| 771 | if s.Current { | 758 | if s.Current { | ||
| 772 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, " aria-current=\"page\"") | 759 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 76, " aria-current=\"page\"") |
| 773 | if templ_7745c5c3_Err != nil { | 760 | if templ_7745c5c3_Err != nil { | ||
| 774 | return templ_7745c5c3_Err | 761 | return templ_7745c5c3_Err | ||
| 775 | } | 762 | } | ||
| 776 | } | 763 | } | ||
| 777 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, ">") | 764 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 77, ">") |
| 778 | if templ_7745c5c3_Err != nil { | 765 | if templ_7745c5c3_Err != nil { | ||
| 779 | return templ_7745c5c3_Err | 766 | return templ_7745c5c3_Err | ||
| 780 | } | 767 | } | ||
| 781 | - | var templ_7745c5c3_Var40 string | 768 | + | var templ_7745c5c3_Var39 string |
| 782 | - | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(s.Label) | 769 | + | templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(s.Label) |
| 783 | if templ_7745c5c3_Err != nil { | 770 | if templ_7745c5c3_Err != nil { | ||
| 784 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 181, Col: 17} | 771 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 180, Col: 17} |
| 785 | } | 772 | } | ||
| 786 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) | 773 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) |
| 787 | if templ_7745c5c3_Err != nil { | 774 | if templ_7745c5c3_Err != nil { | ||
| 788 | return templ_7745c5c3_Err | 775 | return templ_7745c5c3_Err | ||
| 789 | } | 776 | } | ||
| 790 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, " ") | 777 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 78, " ") |
| 791 | if templ_7745c5c3_Err != nil { | 778 | if templ_7745c5c3_Err != nil { | ||
| 792 | return templ_7745c5c3_Err | 779 | return templ_7745c5c3_Err | ||
| 793 | } | 780 | } | ||
| 794 | if s.Count != "" { | 781 | if s.Count != "" { | ||
| 795 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<span class=\"count\">") | 782 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 79, "<span class=\"count\">") |
| 796 | if templ_7745c5c3_Err != nil { | 783 | if templ_7745c5c3_Err != nil { | ||
| 797 | return templ_7745c5c3_Err | 784 | return templ_7745c5c3_Err | ||
| 798 | } | 785 | } | ||
| 799 | - | var templ_7745c5c3_Var41 string | 786 | + | var templ_7745c5c3_Var40 string |
| 800 | - | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(s.Count) | 787 | + | templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(s.Count) |
| 801 | if templ_7745c5c3_Err != nil { | 788 | if templ_7745c5c3_Err != nil { | ||
| 802 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 183, Col: 38} | 789 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 182, Col: 38} |
| 803 | } | 790 | } | ||
| 804 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) | 791 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) |
| 805 | if templ_7745c5c3_Err != nil { | 792 | if templ_7745c5c3_Err != nil { | ||
| 806 | return templ_7745c5c3_Err | 793 | return templ_7745c5c3_Err | ||
| 807 | } | 794 | } | ||
| 808 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "<span class=\"sr\">") | 795 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 80, "<span class=\"sr\">") |
| 809 | if templ_7745c5c3_Err != nil { | 796 | if templ_7745c5c3_Err != nil { | ||
| 810 | return templ_7745c5c3_Err | 797 | return templ_7745c5c3_Err | ||
| 811 | } | 798 | } | ||
| 812 | - | var templ_7745c5c3_Var42 string | 799 | + | var templ_7745c5c3_Var41 string |
| 813 | - | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(" open") | 800 | + | templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(" open") |
| 814 | if templ_7745c5c3_Err != nil { | 801 | if templ_7745c5c3_Err != nil { | ||
| 815 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 183, Col: 66} | 802 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 182, Col: 66} |
| 816 | } | 803 | } | ||
| 817 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) | 804 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) |
| 818 | if templ_7745c5c3_Err != nil { | 805 | if templ_7745c5c3_Err != nil { | ||
| 819 | return templ_7745c5c3_Err | 806 | return templ_7745c5c3_Err | ||
| 820 | } | 807 | } | ||
| 821 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "</span></span> ") | 808 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 81, "</span></span> ") |
| 822 | if templ_7745c5c3_Err != nil { | 809 | if templ_7745c5c3_Err != nil { | ||
| 823 | return templ_7745c5c3_Err | 810 | return templ_7745c5c3_Err | ||
| 824 | } | 811 | } | ||
| 825 | } | 812 | } | ||
| 826 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "<kbd aria-hidden=\"true\">") | 813 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 82, "<kbd aria-hidden=\"true\">") |
| 827 | if templ_7745c5c3_Err != nil { | 814 | if templ_7745c5c3_Err != nil { | ||
| 828 | return templ_7745c5c3_Err | 815 | return templ_7745c5c3_Err | ||
| 829 | } | 816 | } | ||
| 830 | - | var templ_7745c5c3_Var43 string | 817 | + | var templ_7745c5c3_Var42 string |
| 831 | - | templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs("g " + s.Key) | 818 | + | templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs("g " + s.Key) |
| 832 | if templ_7745c5c3_Err != nil { | 819 | if templ_7745c5c3_Err != nil { | ||
| 833 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 185, Col: 46} | 820 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 184, Col: 46} |
| 834 | } | 821 | } | ||
| 835 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) | 822 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) |
| 836 | if templ_7745c5c3_Err != nil { | 823 | if templ_7745c5c3_Err != nil { | ||
| 837 | return templ_7745c5c3_Err | 824 | return templ_7745c5c3_Err | ||
| 838 | } | 825 | } | ||
| 839 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</kbd></a></li>") | 826 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 83, "</kbd></a></li>") |
| 840 | if templ_7745c5c3_Err != nil { | 827 | if templ_7745c5c3_Err != nil { | ||
| 841 | return templ_7745c5c3_Err | 828 | return templ_7745c5c3_Err | ||
| 842 | } | 829 | } | ||
| 843 | } | 830 | } | ||
| 844 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</ul></nav>") | 831 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 84, "</ul></nav>") |
| 845 | if templ_7745c5c3_Err != nil { | 832 | if templ_7745c5c3_Err != nil { | ||
| 846 | return templ_7745c5c3_Err | 833 | return templ_7745c5c3_Err | ||
| 847 | } | 834 | } | ||
| 848 | } | 835 | } | ||
| 849 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "</header>") | 836 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 85, "</header>") |
| 850 | if templ_7745c5c3_Err != nil { | 837 | if templ_7745c5c3_Err != nil { | ||
| 851 | return templ_7745c5c3_Err | 838 | return templ_7745c5c3_Err | ||
| 852 | } | 839 | } | ||
| @@ -872,38 +859,38 @@ func Footer(l Layout) templ.Component { | |||||
| 872 | }() | 859 | }() | ||
| 873 | } | 860 | } | ||
| 874 | ctx = templ.InitializeContext(ctx) | 861 | ctx = templ.InitializeContext(ctx) | ||
| 875 | - | templ_7745c5c3_Var44 := templ.GetChildren(ctx) | 862 | + | templ_7745c5c3_Var43 := templ.GetChildren(ctx) |
| 876 | - | if templ_7745c5c3_Var44 == nil { | 863 | + | if templ_7745c5c3_Var43 == nil { |
| 877 | - | templ_7745c5c3_Var44 = templ.NopComponent | 864 | + | templ_7745c5c3_Var43 = templ.NopComponent |
| 878 | } | 865 | } | ||
| 879 | ctx = templ.ClearChildren(ctx) | 866 | ctx = templ.ClearChildren(ctx) | ||
| 880 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "<footer class=\"foot\"><span>ermite ") | 867 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 86, "<footer class=\"foot\"><span>ermite ") |
| 881 | if templ_7745c5c3_Err != nil { | 868 | if templ_7745c5c3_Err != nil { | ||
| 882 | return templ_7745c5c3_Err | 869 | return templ_7745c5c3_Err | ||
| 883 | } | 870 | } | ||
| 884 | - | var templ_7745c5c3_Var45 string | 871 | + | var templ_7745c5c3_Var44 string |
| 885 | - | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(l.Version) | 872 | + | templ_7745c5c3_Var44, templ_7745c5c3_Err = templ.JoinStringErrs(l.Version) |
| 886 | if templ_7745c5c3_Err != nil { | 873 | if templ_7745c5c3_Err != nil { | ||
| 887 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 199, Col: 26} | 874 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 198, Col: 26} |
| 888 | } | 875 | } | ||
| 889 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) | 876 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var44)) |
| 890 | if templ_7745c5c3_Err != nil { | 877 | if templ_7745c5c3_Err != nil { | ||
| 891 | return templ_7745c5c3_Err | 878 | return templ_7745c5c3_Err | ||
| 892 | } | 879 | } | ||
| 893 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "</span> <a href=\"") | 880 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 87, "</span> <a href=\"") |
| 894 | if templ_7745c5c3_Err != nil { | 881 | if templ_7745c5c3_Err != nil { | ||
| 895 | return templ_7745c5c3_Err | 882 | return templ_7745c5c3_Err | ||
| 896 | } | 883 | } | ||
| 897 | - | var templ_7745c5c3_Var46 templ.SafeURL | 884 | + | var templ_7745c5c3_Var45 templ.SafeURL |
| 898 | - | templ_7745c5c3_Var46, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(l.SourceURL)) | 885 | + | templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(l.SourceURL)) |
| 899 | if templ_7745c5c3_Err != nil { | 886 | if templ_7745c5c3_Err != nil { | ||
| 900 | - | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 200, Col: 38} | 887 | + | return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/adapters/web/shell.templ`, Line: 199, Col: 38} |
| 901 | } | 888 | } | ||
| 902 | - | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var46)) | 889 | + | _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) |
| 903 | if templ_7745c5c3_Err != nil { | 890 | if templ_7745c5c3_Err != nil { | ||
| 904 | return templ_7745c5c3_Err | 891 | return templ_7745c5c3_Err | ||
| 905 | } | 892 | } | ||
| 906 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "\" rel=\"noopener\" title=\"source of this version (AGPL-3.0-or-later)\">source</a> <button type=\"button\" class=\"foot-keys\" data-js data-fire=\"help\" aria-haspopup=\"dialog\" aria-controls=\"panel-help\" aria-keyshortcuts=\"?\"><kbd aria-hidden=\"true\">?</kbd> keyboard shortcuts</button></footer>") | 893 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 88, "\" rel=\"noopener\" title=\"source of this version (AGPL-3.0-or-later)\">source</a> <button type=\"button\" class=\"foot-keys\" data-js data-fire=\"help\" aria-haspopup=\"dialog\" aria-controls=\"panel-help\" aria-keyshortcuts=\"?\"><kbd aria-hidden=\"true\">?</kbd> keyboard shortcuts</button></footer>") |
| 907 | if templ_7745c5c3_Err != nil { | 894 | if templ_7745c5c3_Err != nil { | ||
| 908 | return templ_7745c5c3_Err | 895 | return templ_7745c5c3_Err | ||
| 909 | } | 896 | } | ||
| @@ -931,12 +918,12 @@ func Hint(l Layout) templ.Component { | |||||
| 931 | }() | 918 | }() | ||
| 932 | } | 919 | } | ||
| 933 | ctx = templ.InitializeContext(ctx) | 920 | ctx = templ.InitializeContext(ctx) | ||
| 934 | - | templ_7745c5c3_Var47 := templ.GetChildren(ctx) | 921 | + | templ_7745c5c3_Var46 := templ.GetChildren(ctx) |
| 935 | - | if templ_7745c5c3_Var47 == nil { | 922 | + | if templ_7745c5c3_Var46 == nil { |
| 936 | - | templ_7745c5c3_Var47 = templ.NopComponent | 923 | + | templ_7745c5c3_Var46 = templ.NopComponent |
| 937 | } | 924 | } | ||
| 938 | ctx = templ.ClearChildren(ctx) | 925 | ctx = templ.ClearChildren(ctx) | ||
| 939 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<div class=\"hint\" role=\"status\" aria-live=\"polite\"><span class=\"hint-nav\" :text=\"document.body.dataset.nav || ''\"></span> <span class=\"hint-sel\" :text=\"document.body.dataset.sel ? document.body.dataset.sel + ' · y permalink' : ''\"></span> <span class=\"hint-flash\" :text=\"document.body.dataset.flash || ''\"></span>") | 926 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 89, "<div class=\"hint\" role=\"status\" aria-live=\"polite\"><span class=\"hint-nav\" :text=\"document.body.dataset.nav || ''\"></span> <span class=\"hint-sel\" :text=\"document.body.dataset.sel ? document.body.dataset.sel + ' · y permalink' : ''\"></span> <span class=\"hint-flash\" :text=\"document.body.dataset.flash || ''\"></span>") |
| 940 | if templ_7745c5c3_Err != nil { | 927 | if templ_7745c5c3_Err != nil { | ||
| 941 | return templ_7745c5c3_Err | 928 | return templ_7745c5c3_Err | ||
| 942 | } | 929 | } | ||
| @@ -944,7 +931,7 @@ func Hint(l Layout) templ.Component { | |||||
| 944 | if templ_7745c5c3_Err != nil { | 931 | if templ_7745c5c3_Err != nil { | ||
| 945 | return templ_7745c5c3_Err | 932 | return templ_7745c5c3_Err | ||
| 946 | } | 933 | } | ||
| 947 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 91, "</div>") | 934 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</div>") |
| 948 | if templ_7745c5c3_Err != nil { | 935 | if templ_7745c5c3_Err != nil { | ||
| 949 | return templ_7745c5c3_Err | 936 | return templ_7745c5c3_Err | ||
| 950 | } | 937 | } | ||