fix(web): reopen diff files on a layout switch and let x expand them again
- commit
- 5e5d8fbe2752b45a31f22547f931af6c206f0559
- author
- brice · · 2026-10-11 12:46 UTC
- parent
- e84f160
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
8 files · +319 -268
M internal/adapters/web/diff.templ +9 -1
| @@ -55,7 +55,7 @@ templ Commit(l Layout, p CommitPage) { | |||
| 55 | 55 | <span class="grow"></span> | |
| 56 | 56 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | |
| 57 | 57 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | |
| 58 | - | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">Collapse all<kbd aria-hidden="true">x</kbd></button> | |
| 58 | + | @collapseButton() | |
| 59 | 59 | </div> | |
| 60 | 60 | if p.Invisible { | |
| 61 | 61 | <p class="notice"> | |
| @@ -112,6 +112,14 @@ templ viewLink(p DiffView, view, label, key, href string) { | |||
| 112 | 112 | >{ label }<kbd aria-hidden="true">{ key }</kbd></a> | |
| 113 | 113 | } | |
| 114 | 114 | ||
| 115 | + | // collapseButton folds every file of the diff, or opens them all again when | |
| 116 | + | // none is open; its label follows the state. | |
| 117 | + | templ collapseButton() { | |
| 118 | + | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn"> | |
| 119 | + | <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span><kbd aria-hidden="true">x</kbd> | |
| 120 | + | </button> | |
| 121 | + | } | |
| 122 | + | ||
| 115 | 123 | templ personView(pr Person) { | |
| 116 | 124 | { pr.Name } | |
| 117 | 125 | if pr.WhenAt != "" { | |
| @@ -55,7 +55,7 @@ templ Commit(l Layout, p CommitPage) { | |||||
| 55 | <span class="grow"></span> | 55 | <span class="grow"></span> | ||
| 56 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | 56 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | ||
| 57 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | 57 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | ||
| 58 | - | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">Collapse all<kbd aria-hidden="true">x</kbd></button> | 58 | + | @collapseButton() |
| 59 | </div> | 59 | </div> | ||
| 60 | if p.Invisible { | 60 | if p.Invisible { | ||
| 61 | <p class="notice"> | 61 | <p class="notice"> | ||
| @@ -112,6 +112,14 @@ templ viewLink(p DiffView, view, label, key, href string) { | |||||
| 112 | >{ label }<kbd aria-hidden="true">{ key }</kbd></a> | 112 | >{ label }<kbd aria-hidden="true">{ key }</kbd></a> | ||
| 113 | } | 113 | } | ||
| 114 | 114 | ||||
| 115 | + | // collapseButton folds every file of the diff, or opens them all again when | |||
| 116 | + | // none is open; its label follows the state. | |||
| 117 | + | templ collapseButton() { | |||
| 118 | + | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn"> | |||
| 119 | + | <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span><kbd aria-hidden="true">x</kbd> | |||
| 120 | + | </button> | |||
| 121 | + | } | |||
| 122 | + | ||||
| 115 | templ personView(pr Person) { | 123 | templ personView(pr Person) { | ||
| 116 | { pr.Name } | 124 | { pr.Name } | ||
| 117 | if pr.WhenAt != "" { | 125 | if pr.WhenAt != "" { | ||
M internal/adapters/web/diff_templ.go +292 -257 generated file: collapsed
M internal/adapters/web/keys.templ +3 -3
| @@ -52,9 +52,9 @@ templ Keys() { | |||
| 52 | 52 | let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3); | |
| 53 | 53 | let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' }) | |
| 54 | 54 | }; | |
| 55 | - | k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); | |
| 56 | - | k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); | |
| 57 | - | k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false); | |
| 55 | + | k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true); | |
| 56 | + | k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true); | |
| 57 | + | k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) }; | |
| 58 | 58 | k-permalink from:body -> { | |
| 59 | 59 | let b = document.body.dataset; | |
| 60 | 60 | if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return } | |
| @@ -52,9 +52,9 @@ templ Keys() { | |||||
| 52 | let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3); | 52 | let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3); | ||
| 53 | let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' }) | 53 | let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' }) | ||
| 54 | }; | 54 | }; | ||
| 55 | - | k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); | 55 | + | k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true); |
| 56 | - | k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); | 56 | + | k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true); |
| 57 | - | k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false); | 57 | + | k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) }; |
| 58 | k-permalink from:body -> { | 58 | k-permalink from:body -> { | ||
| 59 | let b = document.body.dataset; | 59 | let b = document.body.dataset; | ||
| 60 | if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return } | 60 | if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return } | ||
M internal/adapters/web/keys_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/mr.templ +2 -2
| @@ -168,7 +168,7 @@ templ MergeRequest(l Layout, p MRPage) { | |||
| 168 | 168 | <span class="grow"></span> | |
| 169 | 169 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | |
| 170 | 170 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | |
| 171 | - | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">Collapse all<kbd aria-hidden="true">x</kbd></button> | |
| 171 | + | @collapseButton() | |
| 172 | 172 | </div> | |
| 173 | 173 | if p.Invisible { | |
| 174 | 174 | <p class="notice"> | |
| @@ -448,7 +448,7 @@ templ Compare(l Layout, p ComparePage) { | |||
| 448 | 448 | <span class="grow"></span> | |
| 449 | 449 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | |
| 450 | 450 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | |
| 451 | - | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">Collapse all<kbd aria-hidden="true">x</kbd></button> | |
| 451 | + | @collapseButton() | |
| 452 | 452 | </div> | |
| 453 | 453 | if p.Invisible { | |
| 454 | 454 | <p class="notice"> | |
| @@ -168,7 +168,7 @@ templ MergeRequest(l Layout, p MRPage) { | |||||
| 168 | <span class="grow"></span> | 168 | <span class="grow"></span> | ||
| 169 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | 169 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | ||
| 170 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | 170 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | ||
| 171 | - | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">Collapse all<kbd aria-hidden="true">x</kbd></button> | 171 | + | @collapseButton() |
| 172 | </div> | 172 | </div> | ||
| 173 | if p.Invisible { | 173 | if p.Invisible { | ||
| 174 | <p class="notice"> | 174 | <p class="notice"> | ||
| @@ -448,7 +448,7 @@ templ Compare(l Layout, p ComparePage) { | |||||
| 448 | <span class="grow"></span> | 448 | <span class="grow"></span> | ||
| 449 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | 449 | @viewLink(p.DiffView, "unified", "Unified", "u", p.UnifiedHref) | ||
| 450 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | 450 | @viewLink(p.DiffView, "split", "Split", "d", p.SplitHref) | ||
| 451 | - | <button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">Collapse all<kbd aria-hidden="true">x</kbd></button> | 451 | + | @collapseButton() |
| 452 | </div> | 452 | </div> | ||
| 453 | if p.Invisible { | 453 | if p.Invisible { | ||
| 454 | <p class="notice"> | 454 | <p class="notice"> | ||
M internal/adapters/web/mr_templ.go +10 -2
| @@ -974,7 +974,11 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||
| 974 | 974 | if templ_7745c5c3_Err != nil { | |
| 975 | 975 | return templ_7745c5c3_Err | |
| 976 | 976 | } | |
| 977 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<button type=\"button\" aria-keyshortcuts=\"x\" hx-on:click=\"document.body.dispatchEvent(new CustomEvent('k-collapse'))\" class=\"btn\">Collapse all<kbd aria-hidden=\"true\">x</kbd></button></div>") | |
| 977 | + | templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer) | |
| 978 | + | if templ_7745c5c3_Err != nil { | |
| 979 | + | return templ_7745c5c3_Err | |
| 980 | + | } | |
| 981 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</div>") | |
| 978 | 982 | if templ_7745c5c3_Err != nil { | |
| 979 | 983 | return templ_7745c5c3_Err | |
| 980 | 984 | } | |
| @@ -2444,7 +2448,11 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||
| 2444 | 2448 | if templ_7745c5c3_Err != nil { | |
| 2445 | 2449 | return templ_7745c5c3_Err | |
| 2446 | 2450 | } | |
| 2447 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "<button type=\"button\" aria-keyshortcuts=\"x\" hx-on:click=\"document.body.dispatchEvent(new CustomEvent('k-collapse'))\" class=\"btn\">Collapse all<kbd aria-hidden=\"true\">x</kbd></button></div>") | |
| 2451 | + | templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer) | |
| 2452 | + | if templ_7745c5c3_Err != nil { | |
| 2453 | + | return templ_7745c5c3_Err | |
| 2454 | + | } | |
| 2455 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "</div>") | |
| 2448 | 2456 | if templ_7745c5c3_Err != nil { | |
| 2449 | 2457 | return templ_7745c5c3_Err | |
| 2450 | 2458 | } | |
| @@ -974,7 +974,11 @@ func MergeRequest(l Layout, p MRPage) templ.Component { | |||||
| 974 | if templ_7745c5c3_Err != nil { | 974 | if templ_7745c5c3_Err != nil { | ||
| 975 | return templ_7745c5c3_Err | 975 | return templ_7745c5c3_Err | ||
| 976 | } | 976 | } | ||
| 977 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "<button type=\"button\" aria-keyshortcuts=\"x\" hx-on:click=\"document.body.dispatchEvent(new CustomEvent('k-collapse'))\" class=\"btn\">Collapse all<kbd aria-hidden=\"true\">x</kbd></button></div>") | 977 | + | templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer) |
| 978 | + | if templ_7745c5c3_Err != nil { | |||
| 979 | + | return templ_7745c5c3_Err | |||
| 980 | + | } | |||
| 981 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 90, "</div>") | |||
| 978 | if templ_7745c5c3_Err != nil { | 982 | if templ_7745c5c3_Err != nil { | ||
| 979 | return templ_7745c5c3_Err | 983 | return templ_7745c5c3_Err | ||
| 980 | } | 984 | } | ||
| @@ -2444,7 +2448,11 @@ func Compare(l Layout, p ComparePage) templ.Component { | |||||
| 2444 | if templ_7745c5c3_Err != nil { | 2448 | if templ_7745c5c3_Err != nil { | ||
| 2445 | return templ_7745c5c3_Err | 2449 | return templ_7745c5c3_Err | ||
| 2446 | } | 2450 | } | ||
| 2447 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "<button type=\"button\" aria-keyshortcuts=\"x\" hx-on:click=\"document.body.dispatchEvent(new CustomEvent('k-collapse'))\" class=\"btn\">Collapse all<kbd aria-hidden=\"true\">x</kbd></button></div>") | 2451 | + | templ_7745c5c3_Err = collapseButton().Render(ctx, templ_7745c5c3_Buffer) |
| 2452 | + | if templ_7745c5c3_Err != nil { | |||
| 2453 | + | return templ_7745c5c3_Err | |||
| 2454 | + | } | |||
| 2455 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 217, "</div>") | |||
| 2448 | if templ_7745c5c3_Err != nil { | 2456 | if templ_7745c5c3_Err != nil { | ||
| 2449 | return templ_7745c5c3_Err | 2457 | return templ_7745c5c3_Err | ||
| 2450 | } | 2458 | } | ||
M internal/adapters/web/overlays.templ +1 -1
| @@ -19,7 +19,7 @@ templ OverlayHelp() { | |||
| 19 | 19 | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests</dd></div> | |
| 20 | 20 | <div><dt><kbd>g h</kbd></dt><dd>home</dd></div> | |
| 21 | 21 | <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div> | |
| 22 | - | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse all</dd></div> | |
| 22 | + | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div> | |
| 23 | 23 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | |
| 24 | 24 | <div><dt><kbd>esc</kbd></dt><dd>close a dialog, leave a field</dd></div> | |
| 25 | 25 | </dl> | |
| @@ -19,7 +19,7 @@ templ OverlayHelp() { | |||||
| 19 | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests</dd></div> | 19 | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests</dd></div> | ||
| 20 | <div><dt><kbd>g h</kbd></dt><dd>home</dd></div> | 20 | <div><dt><kbd>g h</kbd></dt><dd>home</dd></div> | ||
| 21 | <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div> | 21 | <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div> | ||
| 22 | - | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse all</dd></div> | 22 | + | <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div> |
| 23 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | 23 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | ||
| 24 | <div><dt><kbd>esc</kbd></dt><dd>close a dialog, leave a field</dd></div> | 24 | <div><dt><kbd>esc</kbd></dt><dd>close a dialog, leave a field</dd></div> | ||
| 25 | </dl> | 25 | </dl> | ||