feat(web): make the wide mode a global w shortcut, set before the first paint, and tighten the merge request header
- commit
- a1f8e85bb4d541294e1ab39619856cc323b9ab05
- author
- brice · · 2026-10-19 15:13 UTC
- parent
- fcc7703
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
10 files · +22 -17
M internal/adapters/web/diff.templ +2 -2
| @@ -131,8 +131,8 @@ templ diffViewMenu(p DiffView) { | |||
| 131 | 131 | </details> | |
| 132 | 132 | } | |
| 133 | 133 | ||
| 134 | - | // wideButton gives the diff the whole width of the screen: the rail goes to | |
| 135 | - | // the right edge and the code takes the rest. ermite.js keeps the choice. | |
| 134 | + | // wideButton turns the wide mode (w, on every page) on or off: the rail goes | |
| 135 | + | // to the right edge and the code takes the rest. ermite.js keeps the choice. | |
| 136 | 136 | templ wideButton() { | |
| 137 | 137 | <button type="button" class="btn" data-js data-fire="wide" data-wide-toggle aria-pressed="false" aria-keyshortcuts="w" title="use the whole width of the screen"> | |
| 138 | 138 | Wide<kbd aria-hidden="true">w</kbd> | |
| @@ -131,8 +131,8 @@ templ diffViewMenu(p DiffView) { | |||||
| 131 | </details> | 131 | </details> | ||
| 132 | } | 132 | } | ||
| 133 | 133 | ||||
| 134 | - | // wideButton gives the diff the whole width of the screen: the rail goes to | 134 | + | // wideButton turns the wide mode (w, on every page) on or off: the rail goes |
| 135 | - | // the right edge and the code takes the rest. ermite.js keeps the choice. | 135 | + | // to the right edge and the code takes the rest. ermite.js keeps the choice. |
| 136 | templ wideButton() { | 136 | templ wideButton() { | ||
| 137 | <button type="button" class="btn" data-js data-fire="wide" data-wide-toggle aria-pressed="false" aria-keyshortcuts="w" title="use the whole width of the screen"> | 137 | <button type="button" class="btn" data-js data-fire="wide" data-wide-toggle aria-pressed="false" aria-keyshortcuts="w" title="use the whole width of the screen"> | ||
| 138 | Wide<kbd aria-hidden="true">w</kbd> | 138 | Wide<kbd aria-hidden="true">w</kbd> | ||
M internal/adapters/web/diff_templ.go +2 -2 generated file: collapsed
M internal/adapters/web/overlays.templ +1 -1
| @@ -28,9 +28,9 @@ templ OverlayHelp(mr bool) { | |||
| 28 | 28 | <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div> | |
| 29 | 29 | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd></div> | |
| 30 | 30 | <div><dt><kbd>g h</kbd></dt><dd>home</dd></div> | |
| 31 | + | <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div> | |
| 31 | 32 | <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div> | |
| 32 | 33 | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div> | |
| 33 | - | <div><dt><kbd>w</kbd></dt><dd>diff: use the whole width of the screen, or not</dd></div> | |
| 34 | 34 | <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div> | |
| 35 | 35 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | |
| 36 | 36 | if mr { | |
| @@ -28,9 +28,9 @@ templ OverlayHelp(mr bool) { | |||||
| 28 | <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div> | 28 | <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div> | ||
| 29 | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd></div> | 29 | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd></div> | ||
| 30 | <div><dt><kbd>g h</kbd></dt><dd>home</dd></div> | 30 | <div><dt><kbd>g h</kbd></dt><dd>home</dd></div> | ||
| 31 | + | <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div> | |||
| 31 | <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div> | 32 | <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div> | ||
| 32 | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div> | 33 | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div> | ||
| 33 | - | <div><dt><kbd>w</kbd></dt><dd>diff: use the whole width of the screen, or not</dd></div> | |||
| 34 | <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div> | 34 | <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div> | ||
| 35 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | 35 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | ||
| 36 | if mr { | 36 | if mr { | ||
M internal/adapters/web/overlays_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/shell.templ +1 -1
| @@ -25,7 +25,7 @@ templ Shell(l Layout, rail templ.Component) { | |||
| 25 | 25 | <script src={ l.Assets.HTMX } defer></script> | |
| 26 | 26 | <script src={ l.Assets.Live } defer></script> | |
| 27 | 27 | <script src={ l.Assets.SSE } defer></script> | |
| 28 | - | <script src={ l.Assets.Store } defer></script> | |
| 28 | + | <script src={ l.Assets.Store }></script> | |
| 29 | 29 | <script src={ l.Assets.App } defer></script> | |
| 30 | 30 | <script src={ l.Assets.Files } defer></script> | |
| 31 | 31 | <script src={ l.Assets.Passkey } defer></script> | |
| @@ -25,7 +25,7 @@ templ Shell(l Layout, rail templ.Component) { | |||||
| 25 | <script src={ l.Assets.HTMX } defer></script> | 25 | <script src={ l.Assets.HTMX } defer></script> | ||
| 26 | <script src={ l.Assets.Live } defer></script> | 26 | <script src={ l.Assets.Live } defer></script> | ||
| 27 | <script src={ l.Assets.SSE } defer></script> | 27 | <script src={ l.Assets.SSE } defer></script> | ||
| 28 | - | <script src={ l.Assets.Store } defer></script> | 28 | + | <script src={ l.Assets.Store }></script> |
| 29 | <script src={ l.Assets.App } defer></script> | 29 | <script src={ l.Assets.App } defer></script> | ||
| 30 | <script src={ l.Assets.Files } defer></script> | 30 | <script src={ l.Assets.Files } defer></script> | ||
| 31 | <script src={ l.Assets.Passkey } defer></script> | 31 | <script src={ l.Assets.Passkey } defer></script> | ||
M internal/adapters/web/shell_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/static/css/20-shell.css +3 -3
| @@ -133,9 +133,9 @@ | |||
| 133 | 133 | align-content: start; | |
| 134 | 134 | } | |
| 135 | 135 | .work.has-rail { grid-template-columns: minmax(0, 1fr) var(--rail); } | |
| 136 | - | /* the wide diff (w, ermiteWide): a page with a diff takes the whole width | |
| 137 | - | of the screen; the rail goes to the right edge, the code takes the rest */ | |
| 138 | - | html.wide body:has(#diffs) :is(.work, .hdr-top, .tabs, .foot) { max-width: none; } | |
| 136 | + | /* the wide mode (w, ermiteWide): every page takes the whole width of the | |
| 137 | + | screen; the rail goes to the right edge, the content takes the rest */ | |
| 138 | + | html.wide :is(.work, .hdr-top, .tabs, .foot) { max-width: none; } | |
| 139 | 139 | .rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; } | |
| 140 | 140 | .work-lede { grid-column: 1 / -1; margin-bottom: calc(-1 * var(--s2)); } | |
| 141 | 141 | main > * + * { margin-top: var(--s4); } | |
| @@ -133,9 +133,9 @@ | |||||
| 133 | align-content: start; | 133 | align-content: start; | ||
| 134 | } | 134 | } | ||
| 135 | .work.has-rail { grid-template-columns: minmax(0, 1fr) var(--rail); } | 135 | .work.has-rail { grid-template-columns: minmax(0, 1fr) var(--rail); } | ||
| 136 | - | /* the wide diff (w, ermiteWide): a page with a diff takes the whole width | 136 | + | /* the wide mode (w, ermiteWide): every page takes the whole width of the |
| 137 | - | of the screen; the rail goes to the right edge, the code takes the rest */ | 137 | + | screen; the rail goes to the right edge, the content takes the rest */ |
| 138 | - | html.wide body:has(#diffs) :is(.work, .hdr-top, .tabs, .foot) { max-width: none; } | 138 | + | html.wide :is(.work, .hdr-top, .tabs, .foot) { max-width: none; } |
| 139 | .rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; } | 139 | .rail { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; } | ||
| 140 | .work-lede { grid-column: 1 / -1; margin-bottom: calc(-1 * var(--s2)); } | 140 | .work-lede { grid-column: 1 / -1; margin-bottom: calc(-1 * var(--s2)); } | ||
| 141 | main > * + * { margin-top: var(--s4); } | 141 | main > * + * { margin-top: var(--s4); } | ||
M internal/adapters/web/static/css/60-mr.css +1 -1
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | /* one card: the header (title, facts) a shade lighter, the description below */ | |
| 5 | 5 | .mr-card { | |
| 6 | 6 | background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; | |
| 7 | - | & > .mr-h { background: var(--bg-raised); border: 0; border-radius: 0; } | |
| 7 | + | & > .mr-h { background: var(--bg-raised); border: 0; border-radius: 0; padding-bottom: var(--s3); } | |
| 8 | 8 | & > .mr-desc.prose { border: 0; border-top: 1px solid var(--border); border-radius: 0; } | |
| 9 | 9 | } | |
| 10 | 10 | ||
| @@ -4,7 +4,7 @@ | |||||
| 4 | /* one card: the header (title, facts) a shade lighter, the description below */ | 4 | /* one card: the header (title, facts) a shade lighter, the description below */ | ||
| 5 | .mr-card { | 5 | .mr-card { | ||
| 6 | background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; | 6 | background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--r); overflow: hidden; | ||
| 7 | - | & > .mr-h { background: var(--bg-raised); border: 0; border-radius: 0; } | 7 | + | & > .mr-h { background: var(--bg-raised); border: 0; border-radius: 0; padding-bottom: var(--s3); } |
| 8 | & > .mr-desc.prose { border: 0; border-top: 1px solid var(--border); border-radius: 0; } | 8 | & > .mr-desc.prose { border: 0; border-top: 1px solid var(--border); border-radius: 0; } | ||
| 9 | } | 9 | } | ||
| 10 | 10 | ||||
M internal/adapters/web/static/js/ermite.js +5 -5
| @@ -14,7 +14,7 @@ | |||
| 14 | 14 | // 7. The service worker (ermiteInstall). | |
| 15 | 15 | // 8. Chip lists kept on one line, with a … chip (ermiteFit). | |
| 16 | 16 | // 9. The layout of the home page, remembered in the browser (ermiteLayout). | |
| 17 | - | // 10. The wide diff: the page takes the whole width (ermiteWide). | |
| 17 | + | // 10. The wide mode: every page takes the whole width (ermiteWide). | |
| 18 | 18 | // | |
| 19 | 19 | // Preferences are kept by ermiteStore (store.js, loaded first). | |
| 20 | 20 | (() => { | |
| @@ -554,13 +554,13 @@ | |||
| 554 | 554 | return { get: pref.get, set, apply, values }; | |
| 555 | 555 | })(); | |
| 556 | 556 | ||
| 557 | - | // The wide diff: on the pages with a diff, the work area takes the whole | |
| 558 | - | // width of the screen, the rail at the right edge and the code in the rest. | |
| 559 | - | // The choice is kept by ermiteStore ("diff-wide": '1' when wide) and shown | |
| 557 | + | // The wide mode (w, on every page): the work area takes the whole width of | |
| 558 | + | // the screen, the rail at the right edge and the content in the rest. | |
| 559 | + | // The choice is kept by ermiteStore ("wide": '1' when wide) and shown | |
| 560 | 560 | // as html.wide, which the styles follow, and as aria-pressed on the | |
| 561 | 561 | // buttons [data-wide-toggle]. Another tab's change applies here at once. | |
| 562 | 562 | window.ermiteWide = (() => { | |
| 563 | - | const pref = window.ermiteStore.pref('diff-wide', { values: ['1', ''], fallback: '' }); | |
| 563 | + | const pref = window.ermiteStore.pref('wide', { values: ['1', ''], fallback: '' }); | |
| 564 | 564 | function apply() { | |
| 565 | 565 | const wide = pref.get() === '1'; | |
| 566 | 566 | document.documentElement.classList.toggle?.('wide', wide); | |
| @@ -14,7 +14,7 @@ | |||||
| 14 | // 7. The service worker (ermiteInstall). | 14 | // 7. The service worker (ermiteInstall). | ||
| 15 | // 8. Chip lists kept on one line, with a … chip (ermiteFit). | 15 | // 8. Chip lists kept on one line, with a … chip (ermiteFit). | ||
| 16 | // 9. The layout of the home page, remembered in the browser (ermiteLayout). | 16 | // 9. The layout of the home page, remembered in the browser (ermiteLayout). | ||
| 17 | - | // 10. The wide diff: the page takes the whole width (ermiteWide). | 17 | + | // 10. The wide mode: every page takes the whole width (ermiteWide). |
| 18 | // | 18 | // | ||
| 19 | // Preferences are kept by ermiteStore (store.js, loaded first). | 19 | // Preferences are kept by ermiteStore (store.js, loaded first). | ||
| 20 | (() => { | 20 | (() => { | ||
| @@ -554,13 +554,13 @@ | |||||
| 554 | return { get: pref.get, set, apply, values }; | 554 | return { get: pref.get, set, apply, values }; | ||
| 555 | })(); | 555 | })(); | ||
| 556 | 556 | ||||
| 557 | - | // The wide diff: on the pages with a diff, the work area takes the whole | 557 | + | // The wide mode (w, on every page): the work area takes the whole width of |
| 558 | - | // width of the screen, the rail at the right edge and the code in the rest. | 558 | + | // the screen, the rail at the right edge and the content in the rest. |
| 559 | - | // The choice is kept by ermiteStore ("diff-wide": '1' when wide) and shown | 559 | + | // The choice is kept by ermiteStore ("wide": '1' when wide) and shown |
| 560 | // as html.wide, which the styles follow, and as aria-pressed on the | 560 | // as html.wide, which the styles follow, and as aria-pressed on the | ||
| 561 | // buttons [data-wide-toggle]. Another tab's change applies here at once. | 561 | // buttons [data-wide-toggle]. Another tab's change applies here at once. | ||
| 562 | window.ermiteWide = (() => { | 562 | window.ermiteWide = (() => { | ||
| 563 | - | const pref = window.ermiteStore.pref('diff-wide', { values: ['1', ''], fallback: '' }); | 563 | + | const pref = window.ermiteStore.pref('wide', { values: ['1', ''], fallback: '' }); |
| 564 | function apply() { | 564 | function apply() { | ||
| 565 | const wide = pref.get() === '1'; | 565 | const wide = pref.get() === '1'; | ||
| 566 | document.documentElement.classList.toggle?.('wide', wide); | 566 | document.documentElement.classList.toggle?.('wide', wide); | ||
M internal/adapters/web/static/js/store.js +5 -0
| @@ -74,3 +74,8 @@ window.ermiteStore = (() => { | |||
| 74 | 74 | ||
| 75 | 75 | return { get, set, remove, watch, pref }; | |
| 76 | 76 | })(); | |
| 77 | + | ||
| 78 | + | // The wide mode (ermiteWide in ermite.js) is set here, before the first paint: | |
| 79 | + | // shell.templ loads this file without defer, so a wide page never draws narrow | |
| 80 | + | // first. | |
| 81 | + | if (window.ermiteStore.get('wide') === '1') document?.documentElement?.classList?.add('wide'); | |
| @@ -74,3 +74,8 @@ window.ermiteStore = (() => { | |||||
| 74 | 74 | ||||
| 75 | return { get, set, remove, watch, pref }; | 75 | return { get, set, remove, watch, pref }; | ||
| 76 | })(); | 76 | })(); | ||
| 77 | + | ||||
| 78 | + | // The wide mode (ermiteWide in ermite.js) is set here, before the first paint: | |||
| 79 | + | // shell.templ loads this file without defer, so a wide page never draws narrow | |||
| 80 | + | // first. | |||
| 81 | + | if (window.ermiteStore.get('wide') === '1') document?.documentElement?.classList?.add('wide'); | |||