detached 08e6388

feat(web): show the latest tag in its own summary rail block

commit
08e63884b6d8316c4c508c604d5fcdfdd75f48bc
author
brice · · 2026-10-11 12:46 UTC
parent
5e5d8fb
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
37 files · +500 -297
M cmd/ermite/testdata/web/blob-binary.html +4 -4
@@ -209,7 +209,7 @@
209209 <kbd>u</kbd>
210210 <kbd>x</kbd>
211211 </dt>
212-<dd>diff: split / unified / collapse all</dd>
212+<dd>diff: split / unified / collapse or expand all</dd>
213213 </div>
214214 <div>
215215 <dt>
@@ -383,9 +383,9 @@
383383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
384384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
385385 };
386- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
387- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
388- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
386+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
387+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
388+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
389389 k-permalink from:body -> {
390390 let b = document.body.dataset;
391391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -209,7 +209,7 @@
209 <kbd>u</kbd>209 <kbd>u</kbd>
210 <kbd>x</kbd>210 <kbd>x</kbd>
211 </dt>211 </dt>
212-<dd>diff: split / unified / collapse all</dd>212+<dd>diff: split / unified / collapse or expand all</dd>
213 </div>213 </div>
214 <div>214 <div>
215 <dt>215 <dt>
@@ -383,9 +383,9 @@
383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
385 };385 };
386- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');386+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
387- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');387+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
388- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);388+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
389 k-permalink from:body -> {389 k-permalink from:body -> {
390 let b = document.body.dataset;390 let b = document.body.dataset;
391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-code.html +4 -4
@@ -279,7 +279,7 @@
279279 <kbd>u</kbd>
280280 <kbd>x</kbd>
281281 </dt>
282-<dd>diff: split / unified / collapse all</dd>
282+<dd>diff: split / unified / collapse or expand all</dd>
283283 </div>
284284 <div>
285285 <dt>
@@ -453,9 +453,9 @@
453453 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
454454 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
455455 };
456- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
457- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
458- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
456+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
457+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
458+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
459459 k-permalink from:body -> {
460460 let b = document.body.dataset;
461461 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -279,7 +279,7 @@
279 <kbd>u</kbd>279 <kbd>u</kbd>
280 <kbd>x</kbd>280 <kbd>x</kbd>
281 </dt>281 </dt>
282-<dd>diff: split / unified / collapse all</dd>282+<dd>diff: split / unified / collapse or expand all</dd>
283 </div>283 </div>
284 <div>284 <div>
285 <dt>285 <dt>
@@ -453,9 +453,9 @@
453 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);453 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
454 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })454 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
455 };455 };
456- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');456+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
457- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');457+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
458- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);458+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
459 k-permalink from:body -> {459 k-permalink from:body -> {
460 let b = document.body.dataset;460 let b = document.body.dataset;
461 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }461 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-empty.html +4 -4
@@ -209,7 +209,7 @@
209209 <kbd>u</kbd>
210210 <kbd>x</kbd>
211211 </dt>
212-<dd>diff: split / unified / collapse all</dd>
212+<dd>diff: split / unified / collapse or expand all</dd>
213213 </div>
214214 <div>
215215 <dt>
@@ -383,9 +383,9 @@
383383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
384384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
385385 };
386- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
387- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
388- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
386+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
387+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
388+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
389389 k-permalink from:body -> {
390390 let b = document.body.dataset;
391391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -209,7 +209,7 @@
209 <kbd>u</kbd>209 <kbd>u</kbd>
210 <kbd>x</kbd>210 <kbd>x</kbd>
211 </dt>211 </dt>
212-<dd>diff: split / unified / collapse all</dd>212+<dd>diff: split / unified / collapse or expand all</dd>
213 </div>213 </div>
214 <div>214 <div>
215 <dt>215 <dt>
@@ -383,9 +383,9 @@
383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
385 };385 };
386- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');386+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
387- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');387+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
388- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);388+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
389 k-permalink from:body -> {389 k-permalink from:body -> {
390 let b = document.body.dataset;390 let b = document.body.dataset;
391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-image.html +4 -4
@@ -211,7 +211,7 @@
211211 <kbd>u</kbd>
212212 <kbd>x</kbd>
213213 </dt>
214-<dd>diff: split / unified / collapse all</dd>
214+<dd>diff: split / unified / collapse or expand all</dd>
215215 </div>
216216 <div>
217217 <dt>
@@ -385,9 +385,9 @@
385385 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
386386 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
387387 };
388- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
389- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
390- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
388+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
389+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
390+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
391391 k-permalink from:body -> {
392392 let b = document.body.dataset;
393393 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -211,7 +211,7 @@
211 <kbd>u</kbd>211 <kbd>u</kbd>
212 <kbd>x</kbd>212 <kbd>x</kbd>
213 </dt>213 </dt>
214-<dd>diff: split / unified / collapse all</dd>214+<dd>diff: split / unified / collapse or expand all</dd>
215 </div>215 </div>
216 <div>216 <div>
217 <dt>217 <dt>
@@ -385,9 +385,9 @@
385 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);385 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
386 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })386 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
387 };387 };
388- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');388+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
389- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');389+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
390- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);390+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
391 k-permalink from:body -> {391 k-permalink from:body -> {
392 let b = document.body.dataset;392 let b = document.body.dataset;
393 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }393 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-markdown.html +4 -4
@@ -276,7 +276,7 @@
276276 <kbd>u</kbd>
277277 <kbd>x</kbd>
278278 </dt>
279-<dd>diff: split / unified / collapse all</dd>
279+<dd>diff: split / unified / collapse or expand all</dd>
280280 </div>
281281 <div>
282282 <dt>
@@ -450,9 +450,9 @@
450450 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
451451 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
452452 };
453- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
454- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
455- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
453+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
454+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
455+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
456456 k-permalink from:body -> {
457457 let b = document.body.dataset;
458458 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -276,7 +276,7 @@
276 <kbd>u</kbd>276 <kbd>u</kbd>
277 <kbd>x</kbd>277 <kbd>x</kbd>
278 </dt>278 </dt>
279-<dd>diff: split / unified / collapse all</dd>279+<dd>diff: split / unified / collapse or expand all</dd>
280 </div>280 </div>
281 <div>281 <div>
282 <dt>282 <dt>
@@ -450,9 +450,9 @@
450 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);450 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
451 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })451 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
452 };452 };
453- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');453+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
454- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');454+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
455- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);455+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
456 k-permalink from:body -> {456 k-permalink from:body -> {
457 let b = document.body.dataset;457 let b = document.body.dataset;
458 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }458 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-svg.html +4 -4
@@ -234,7 +234,7 @@
234234 <kbd>u</kbd>
235235 <kbd>x</kbd>
236236 </dt>
237-<dd>diff: split / unified / collapse all</dd>
237+<dd>diff: split / unified / collapse or expand all</dd>
238238 </div>
239239 <div>
240240 <dt>
@@ -408,9 +408,9 @@
408408 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
409409 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
410410 };
411- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
412- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
413- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
411+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
412+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
413+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
414414 k-permalink from:body -> {
415415 let b = document.body.dataset;
416416 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -234,7 +234,7 @@
234 <kbd>u</kbd>234 <kbd>u</kbd>
235 <kbd>x</kbd>235 <kbd>x</kbd>
236 </dt>236 </dt>
237-<dd>diff: split / unified / collapse all</dd>237+<dd>diff: split / unified / collapse or expand all</dd>
238 </div>238 </div>
239 <div>239 <div>
240 <dt>240 <dt>
@@ -408,9 +408,9 @@
408 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);408 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
409 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })409 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
410 };410 };
411- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');411+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
412- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');412+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
413- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);413+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
414 k-permalink from:body -> {414 k-permalink from:body -> {
415 let b = document.body.dataset;415 let b = document.body.dataset;
416 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }416 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-text.html +4 -4
@@ -229,7 +229,7 @@
229229 <kbd>u</kbd>
230230 <kbd>x</kbd>
231231 </dt>
232-<dd>diff: split / unified / collapse all</dd>
232+<dd>diff: split / unified / collapse or expand all</dd>
233233 </div>
234234 <div>
235235 <dt>
@@ -403,9 +403,9 @@
403403 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
404404 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
405405 };
406- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
407- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
408- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
406+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
407+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
408+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
409409 k-permalink from:body -> {
410410 let b = document.body.dataset;
411411 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -229,7 +229,7 @@
229 <kbd>u</kbd>229 <kbd>u</kbd>
230 <kbd>x</kbd>230 <kbd>x</kbd>
231 </dt>231 </dt>
232-<dd>diff: split / unified / collapse all</dd>232+<dd>diff: split / unified / collapse or expand all</dd>
233 </div>233 </div>
234 <div>234 <div>
235 <dt>235 <dt>
@@ -403,9 +403,9 @@
403 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);403 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
404 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })404 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
405 };405 };
406- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');406+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
407- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');407+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
408- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);408+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
409 k-permalink from:body -> {409 k-permalink from:body -> {
410 let b = document.body.dataset;410 let b = document.body.dataset;
411 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }411 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-too-large.html +4 -4
@@ -209,7 +209,7 @@
209209 <kbd>u</kbd>
210210 <kbd>x</kbd>
211211 </dt>
212-<dd>diff: split / unified / collapse all</dd>
212+<dd>diff: split / unified / collapse or expand all</dd>
213213 </div>
214214 <div>
215215 <dt>
@@ -383,9 +383,9 @@
383383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
384384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
385385 };
386- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
387- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
388- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
386+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
387+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
388+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
389389 k-permalink from:body -> {
390390 let b = document.body.dataset;
391391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -209,7 +209,7 @@
209 <kbd>u</kbd>209 <kbd>u</kbd>
210 <kbd>x</kbd>210 <kbd>x</kbd>
211 </dt>211 </dt>
212-<dd>diff: split / unified / collapse all</dd>212+<dd>diff: split / unified / collapse or expand all</dd>
213 </div>213 </div>
214 <div>214 <div>
215 <dt>215 <dt>
@@ -383,9 +383,9 @@
383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);383 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })384 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
385 };385 };
386- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');386+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
387- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');387+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
388- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);388+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
389 k-permalink from:body -> {389 k-permalink from:body -> {
390 let b = document.body.dataset;390 let b = document.body.dataset;
391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }391 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/blob-trojan.html +4 -4
@@ -276,7 +276,7 @@
276276 <kbd>u</kbd>
277277 <kbd>x</kbd>
278278 </dt>
279-<dd>diff: split / unified / collapse all</dd>
279+<dd>diff: split / unified / collapse or expand all</dd>
280280 </div>
281281 <div>
282282 <dt>
@@ -450,9 +450,9 @@
450450 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
451451 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
452452 };
453- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
454- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
455- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
453+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
454+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
455+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
456456 k-permalink from:body -> {
457457 let b = document.body.dataset;
458458 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -276,7 +276,7 @@
276 <kbd>u</kbd>276 <kbd>u</kbd>
277 <kbd>x</kbd>277 <kbd>x</kbd>
278 </dt>278 </dt>
279-<dd>diff: split / unified / collapse all</dd>279+<dd>diff: split / unified / collapse or expand all</dd>
280 </div>280 </div>
281 <div>281 <div>
282 <dt>282 <dt>
@@ -450,9 +450,9 @@
450 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);450 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
451 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })451 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
452 };452 };
453- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');453+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
454- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');454+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
455- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);455+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
456 k-permalink from:body -> {456 k-permalink from:body -> {
457 let b = document.body.dataset;457 let b = document.body.dataset;
458 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }458 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-big.html +7 -5
@@ -109,7 +109,9 @@
109109 </a>
110110 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
111111 </a>
112-<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>
112+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
113+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
114+<kbd aria-hidden="true">x</kbd>
113115 </button>
114116 </div>
115117 <p class="notice">this diff is incomplete: 1 file is too large to show. the totals above count everything; clone the repository to read the rest.</p>
@@ -244,7 +246,7 @@
244246 <kbd>u</kbd>
245247 <kbd>x</kbd>
246248 </dt>
247-<dd>diff: split / unified / collapse all</dd>
249+<dd>diff: split / unified / collapse or expand all</dd>
248250 </div>
249251 <div>
250252 <dt>
@@ -418,9 +420,9 @@
418420 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
419421 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
420422 };
421- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
422- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
423- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
423+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
424+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
425+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
424426 k-permalink from:body -> {
425427 let b = document.body.dataset;
426428 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -109,7 +109,9 @@
109 </a>109 </a>
110 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>110 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
111 </a>111 </a>
112-<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>112+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
113+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
114+<kbd aria-hidden="true">x</kbd>
113 </button>115 </button>
114 </div>116 </div>
115 <p class="notice">this diff is incomplete: 1 file is too large to show. the totals above count everything; clone the repository to read the rest.</p>117 <p class="notice">this diff is incomplete: 1 file is too large to show. the totals above count everything; clone the repository to read the rest.</p>
@@ -244,7 +246,7 @@
244 <kbd>u</kbd>246 <kbd>u</kbd>
245 <kbd>x</kbd>247 <kbd>x</kbd>
246 </dt>248 </dt>
247-<dd>diff: split / unified / collapse all</dd>249+<dd>diff: split / unified / collapse or expand all</dd>
248 </div>250 </div>
249 <div>251 <div>
250 <dt>252 <dt>
@@ -418,9 +420,9 @@
418 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);420 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
419 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })421 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
420 };422 };
421- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');423+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
422- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');424+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
423- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);425+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
424 k-permalink from:body -> {426 k-permalink from:body -> {
425 let b = document.body.dataset;427 let b = document.body.dataset;
426 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }428 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-binary.html +7 -5
@@ -105,7 +105,9 @@
105105 </a>
106106 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
107107 </a>
108-<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>
108+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
109+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
110+<kbd aria-hidden="true">x</kbd>
109111 </button>
110112 </div>
111113 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -239,7 +241,7 @@
239241 <kbd>u</kbd>
240242 <kbd>x</kbd>
241243 </dt>
242-<dd>diff: split / unified / collapse all</dd>
244+<dd>diff: split / unified / collapse or expand all</dd>
243245 </div>
244246 <div>
245247 <dt>
@@ -413,9 +415,9 @@
413415 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
414416 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
415417 };
416- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
417- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
418- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
418+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
419+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
420+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
419421 k-permalink from:body -> {
420422 let b = document.body.dataset;
421423 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -105,7 +105,9 @@
105 </a>105 </a>
106 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>106 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
107 </a>107 </a>
108-<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>108+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
109+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
110+<kbd aria-hidden="true">x</kbd>
109 </button>111 </button>
110 </div>112 </div>
111 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">113 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -239,7 +241,7 @@
239 <kbd>u</kbd>241 <kbd>u</kbd>
240 <kbd>x</kbd>242 <kbd>x</kbd>
241 </dt>243 </dt>
242-<dd>diff: split / unified / collapse all</dd>244+<dd>diff: split / unified / collapse or expand all</dd>
243 </div>245 </div>
244 <div>246 <div>
245 <dt>247 <dt>
@@ -413,9 +415,9 @@
413 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);415 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
414 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })416 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
415 };417 };
416- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');418+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
417- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');419+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
418- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);420+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
419 k-permalink from:body -> {421 k-permalink from:body -> {
420 let b = document.body.dataset;422 let b = document.body.dataset;
421 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }423 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-merge.html +7 -5
@@ -104,7 +104,9 @@
104104 </a>
105105 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
106106 </a>
107-<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>
107+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
108+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
109+<kbd aria-hidden="true">x</kbd>
108110 </button>
109111 </div>
110112 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -278,7 +280,7 @@
278280 <kbd>u</kbd>
279281 <kbd>x</kbd>
280282 </dt>
281-<dd>diff: split / unified / collapse all</dd>
283+<dd>diff: split / unified / collapse or expand all</dd>
282284 </div>
283285 <div>
284286 <dt>
@@ -452,9 +454,9 @@
452454 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
453455 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
454456 };
455- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
456- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
457- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
457+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
458+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
459+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
458460 k-permalink from:body -> {
459461 let b = document.body.dataset;
460462 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -104,7 +104,9 @@
104 </a>104 </a>
105 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>105 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
106 </a>106 </a>
107-<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>107+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
108+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
109+<kbd aria-hidden="true">x</kbd>
108 </button>110 </button>
109 </div>111 </div>
110 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">112 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -278,7 +280,7 @@
278 <kbd>u</kbd>280 <kbd>u</kbd>
279 <kbd>x</kbd>281 <kbd>x</kbd>
280 </dt>282 </dt>
281-<dd>diff: split / unified / collapse all</dd>283+<dd>diff: split / unified / collapse or expand all</dd>
282 </div>284 </div>
283 <div>285 <div>
284 <dt>286 <dt>
@@ -452,9 +454,9 @@
452 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);454 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
453 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })455 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
454 };456 };
455- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');457+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
456- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');458+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
457- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);459+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
458 k-permalink from:body -> {460 k-permalink from:body -> {
459 let b = document.body.dataset;461 let b = document.body.dataset;
460 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }462 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-modify-split.html +7 -5
@@ -111,7 +111,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
111111 </a>
112112 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" aria-current="true" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
113113 </a>
114-<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>
114+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
115+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
116+<kbd aria-hidden="true">x</kbd>
115117 </button>
116118 </div>
117119 <div id="diffs" data-diff="split">
@@ -399,7 +401,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
399401 <kbd>u</kbd>
400402 <kbd>x</kbd>
401403 </dt>
402-<dd>diff: split / unified / collapse all</dd>
404+<dd>diff: split / unified / collapse or expand all</dd>
403405 </div>
404406 <div>
405407 <dt>
@@ -573,9 +575,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
573575 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
574576 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
575577 };
576- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
577- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
578- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
578+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
579+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
580+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
579581 k-permalink from:body -> {
580582 let b = document.body.dataset;
581583 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -111,7 +111,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
111 </a>111 </a>
112 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" aria-current="true" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>112 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" aria-current="true" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
113 </a>113 </a>
114-<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>114+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
115+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
116+<kbd aria-hidden="true">x</kbd>
115 </button>117 </button>
116 </div>118 </div>
117 <div id="diffs" data-diff="split">119 <div id="diffs" data-diff="split">
@@ -399,7 +401,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
399 <kbd>u</kbd>401 <kbd>u</kbd>
400 <kbd>x</kbd>402 <kbd>x</kbd>
401 </dt>403 </dt>
402-<dd>diff: split / unified / collapse all</dd>404+<dd>diff: split / unified / collapse or expand all</dd>
403 </div>405 </div>
404 <div>406 <div>
405 <dt>407 <dt>
@@ -573,9 +575,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
573 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);575 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
574 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })576 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
575 };577 };
576- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');578+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
577- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');579+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
578- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);580+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
579 k-permalink from:body -> {581 k-permalink from:body -> {
580 let b = document.body.dataset;582 let b = document.body.dataset;
581 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }583 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-modify.html +7 -5
@@ -111,7 +111,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
111111 </a>
112112 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
113113 </a>
114-<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>
114+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
115+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
116+<kbd aria-hidden="true">x</kbd>
115117 </button>
116118 </div>
117119 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -399,7 +401,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
399401 <kbd>u</kbd>
400402 <kbd>x</kbd>
401403 </dt>
402-<dd>diff: split / unified / collapse all</dd>
404+<dd>diff: split / unified / collapse or expand all</dd>
403405 </div>
404406 <div>
405407 <dt>
@@ -573,9 +575,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
573575 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
574576 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
575577 };
576- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
577- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
578- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
578+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
579+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
580+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
579581 k-permalink from:body -> {
580582 let b = document.body.dataset;
581583 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -111,7 +111,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
111 </a>111 </a>
112 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>112 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
113 </a>113 </a>
114-<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>114+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
115+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
116+<kbd aria-hidden="true">x</kbd>
115 </button>117 </button>
116 </div>118 </div>
117 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">119 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -399,7 +401,7 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
399 <kbd>u</kbd>401 <kbd>u</kbd>
400 <kbd>x</kbd>402 <kbd>x</kbd>
401 </dt>403 </dt>
402-<dd>diff: split / unified / collapse all</dd>404+<dd>diff: split / unified / collapse or expand all</dd>
403 </div>405 </div>
404 <div>406 <div>
405 <dt>407 <dt>
@@ -573,9 +575,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
573 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);575 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
574 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })576 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
575 };577 };
576- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');578+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
577- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');579+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
578- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);580+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
579 k-permalink from:body -> {581 k-permalink from:body -> {
580 let b = document.body.dataset;582 let b = document.body.dataset;
581 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }583 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-rename.html +7 -5
@@ -105,7 +105,9 @@
105105 </a>
106106 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
107107 </a>
108-<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>
108+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
109+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
110+<kbd aria-hidden="true">x</kbd>
109111 </button>
110112 </div>
111113 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -304,7 +306,7 @@
304306 <kbd>u</kbd>
305307 <kbd>x</kbd>
306308 </dt>
307-<dd>diff: split / unified / collapse all</dd>
309+<dd>diff: split / unified / collapse or expand all</dd>
308310 </div>
309311 <div>
310312 <dt>
@@ -478,9 +480,9 @@
478480 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
479481 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
480482 };
481- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
482- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
483- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
483+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
484+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
485+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
484486 k-permalink from:body -> {
485487 let b = document.body.dataset;
486488 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -105,7 +105,9 @@
105 </a>105 </a>
106 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>106 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
107 </a>107 </a>
108-<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>108+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
109+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
110+<kbd aria-hidden="true">x</kbd>
109 </button>111 </button>
110 </div>112 </div>
111 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">113 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -304,7 +306,7 @@
304 <kbd>u</kbd>306 <kbd>u</kbd>
305 <kbd>x</kbd>307 <kbd>x</kbd>
306 </dt>308 </dt>
307-<dd>diff: split / unified / collapse all</dd>309+<dd>diff: split / unified / collapse or expand all</dd>
308 </div>310 </div>
309 <div>311 <div>
310 <dt>312 <dt>
@@ -478,9 +480,9 @@
478 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);480 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
479 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })481 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
480 };482 };
481- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');483+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
482- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');484+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
483- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);485+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
484 k-permalink from:body -> {486 k-permalink from:body -> {
485 let b = document.body.dataset;487 let b = document.body.dataset;
486 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }488 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-root.html +7 -5
@@ -103,7 +103,9 @@
103103 </a>
104104 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
105105 </a>
106-<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>
106+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
107+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
108+<kbd aria-hidden="true">x</kbd>
107109 </button>
108110 </div>
109111 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -620,7 +622,7 @@
620622 <kbd>u</kbd>
621623 <kbd>x</kbd>
622624 </dt>
623-<dd>diff: split / unified / collapse all</dd>
625+<dd>diff: split / unified / collapse or expand all</dd>
624626 </div>
625627 <div>
626628 <dt>
@@ -794,9 +796,9 @@
794796 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
795797 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
796798 };
797- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
798- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
799- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
799+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
800+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
801+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
800802 k-permalink from:body -> {
801803 let b = document.body.dataset;
802804 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -103,7 +103,9 @@
103 </a>103 </a>
104 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>104 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
105 </a>105 </a>
106-<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>106+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
107+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
108+<kbd aria-hidden="true">x</kbd>
107 </button>109 </button>
108 </div>110 </div>
109 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">111 <div id="diffs" data-diff="unified" hx-live="this.dataset.diff = sessionStorage.getItem('diff') || this.dataset.diff">
@@ -620,7 +622,7 @@
620 <kbd>u</kbd>622 <kbd>u</kbd>
621 <kbd>x</kbd>623 <kbd>x</kbd>
622 </dt>624 </dt>
623-<dd>diff: split / unified / collapse all</dd>625+<dd>diff: split / unified / collapse or expand all</dd>
624 </div>626 </div>
625 <div>627 <div>
626 <dt>628 <dt>
@@ -794,9 +796,9 @@
794 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);796 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
795 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })797 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
796 };798 };
797- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');799+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
798- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');800+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
799- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);801+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
800 k-permalink from:body -> {802 k-permalink from:body -> {
801 let b = document.body.dataset;803 let b = document.body.dataset;
802 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }804 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commit-trojan.html +7 -5
@@ -106,7 +106,9 @@
106106 </a>
107107 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
108108 </a>
109-<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>
109+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
110+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
111+<kbd aria-hidden="true">x</kbd>
110112 </button>
111113 </div>
112114 <p class="notice">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class="inv">&lt;U+XXXX&gt;</span> markers, never applied.</p>
@@ -413,7 +415,7 @@
413415 <kbd>u</kbd>
414416 <kbd>x</kbd>
415417 </dt>
416-<dd>diff: split / unified / collapse all</dd>
418+<dd>diff: split / unified / collapse or expand all</dd>
417419 </div>
418420 <div>
419421 <dt>
@@ -587,9 +589,9 @@
587589 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
588590 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
589591 };
590- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
591- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
592- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
592+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
593+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
594+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
593595 k-permalink from:body -> {
594596 let b = document.body.dataset;
595597 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -106,7 +106,9 @@
106 </a>106 </a>
107 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>107 <a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" hx-on:click="event.preventDefault(); document.body.dispatchEvent(new CustomEvent('k-' + this.dataset.view))" :aria-current="q(&#39;#diffs&#39;).attr[&#39;data-diff&#39;] === &#39;split&#39; ? &#39;true&#39; : false">Split<kbd aria-hidden="true">d</kbd>
108 </a>108 </a>
109-<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>109+<button type="button" aria-keyshortcuts="x" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-collapse'))" class="btn">
110+<span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
111+<kbd aria-hidden="true">x</kbd>
110 </button>112 </button>
111 </div>113 </div>
112 <p class="notice">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class="inv">&lt;U+XXXX&gt;</span> markers, never applied.</p>114 <p class="notice">this diff holds invisible characters (direction controls, zero-width characters). They are shown as <span class="inv">&lt;U+XXXX&gt;</span> markers, never applied.</p>
@@ -413,7 +415,7 @@
413 <kbd>u</kbd>415 <kbd>u</kbd>
414 <kbd>x</kbd>416 <kbd>x</kbd>
415 </dt>417 </dt>
416-<dd>diff: split / unified / collapse all</dd>418+<dd>diff: split / unified / collapse or expand all</dd>
417 </div>419 </div>
418 <div>420 <div>
419 <dt>421 <dt>
@@ -587,9 +589,9 @@
587 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);589 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
588 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })590 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
589 };591 };
590- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');592+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
591- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');593+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
592- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);594+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
593 k-permalink from:body -> {595 k-permalink from:body -> {
594 let b = document.body.dataset;596 let b = document.body.dataset;
595 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }597 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commits-page2.html +4 -4
@@ -238,7 +238,7 @@
238238 <kbd>u</kbd>
239239 <kbd>x</kbd>
240240 </dt>
241-<dd>diff: split / unified / collapse all</dd>
241+<dd>diff: split / unified / collapse or expand all</dd>
242242 </div>
243243 <div>
244244 <dt>
@@ -398,9 +398,9 @@
398398 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
399399 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
400400 };
401- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
402- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
403- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
401+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
402+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
403+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
404404 k-permalink from:body -> {
405405 let b = document.body.dataset;
406406 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -238,7 +238,7 @@
238 <kbd>u</kbd>238 <kbd>u</kbd>
239 <kbd>x</kbd>239 <kbd>x</kbd>
240 </dt>240 </dt>
241-<dd>diff: split / unified / collapse all</dd>241+<dd>diff: split / unified / collapse or expand all</dd>
242 </div>242 </div>
243 <div>243 <div>
244 <dt>244 <dt>
@@ -398,9 +398,9 @@
398 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);398 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
399 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })399 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
400 };400 };
401- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');401+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
402- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');402+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
403- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);403+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
404 k-permalink from:body -> {404 k-permalink from:body -> {
405 let b = document.body.dataset;405 let b = document.body.dataset;
406 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }406 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commits-path.html +4 -4
@@ -253,7 +253,7 @@
253253 <kbd>u</kbd>
254254 <kbd>x</kbd>
255255 </dt>
256-<dd>diff: split / unified / collapse all</dd>
256+<dd>diff: split / unified / collapse or expand all</dd>
257257 </div>
258258 <div>
259259 <dt>
@@ -413,9 +413,9 @@
413413 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
414414 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
415415 };
416- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
417- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
418- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
416+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
417+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
418+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
419419 k-permalink from:body -> {
420420 let b = document.body.dataset;
421421 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -253,7 +253,7 @@
253 <kbd>u</kbd>253 <kbd>u</kbd>
254 <kbd>x</kbd>254 <kbd>x</kbd>
255 </dt>255 </dt>
256-<dd>diff: split / unified / collapse all</dd>256+<dd>diff: split / unified / collapse or expand all</dd>
257 </div>257 </div>
258 <div>258 <div>
259 <dt>259 <dt>
@@ -413,9 +413,9 @@
413 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);413 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
414 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })414 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
415 };415 };
416- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');416+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
417- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');417+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
418- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);418+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
419 k-permalink from:body -> {419 k-permalink from:body -> {
420 let b = document.body.dataset;420 let b = document.body.dataset;
421 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }421 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commits-root.html +4 -4
@@ -1040,7 +1040,7 @@
10401040 <kbd>u</kbd>
10411041 <kbd>x</kbd>
10421042 </dt>
1043-<dd>diff: split / unified / collapse all</dd>
1043+<dd>diff: split / unified / collapse or expand all</dd>
10441044 </div>
10451045 <div>
10461046 <dt>
@@ -1200,9 +1200,9 @@
12001200 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
12011201 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
12021202 };
1203- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
1204- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
1205- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
1203+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
1204+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
1205+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
12061206 k-permalink from:body -> {
12071207 let b = document.body.dataset;
12081208 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -1040,7 +1040,7 @@
1040 <kbd>u</kbd>1040 <kbd>u</kbd>
1041 <kbd>x</kbd>1041 <kbd>x</kbd>
1042 </dt>1042 </dt>
1043-<dd>diff: split / unified / collapse all</dd>1043+<dd>diff: split / unified / collapse or expand all</dd>
1044 </div>1044 </div>
1045 <div>1045 <div>
1046 <dt>1046 <dt>
@@ -1200,9 +1200,9 @@
1200 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);1200 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
1201 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })1201 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
1202 };1202 };
1203- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');1203+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
1204- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');1204+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
1205- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);1205+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
1206 k-permalink from:body -> {1206 k-permalink from:body -> {
1207 let b = document.body.dataset;1207 let b = document.body.dataset;
1208 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }1208 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/commits-small.html +4 -4
@@ -385,7 +385,7 @@
385385 <kbd>u</kbd>
386386 <kbd>x</kbd>
387387 </dt>
388-<dd>diff: split / unified / collapse all</dd>
388+<dd>diff: split / unified / collapse or expand all</dd>
389389 </div>
390390 <div>
391391 <dt>
@@ -565,9 +565,9 @@
565565 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
566566 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
567567 };
568- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
569- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
570- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
568+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
569+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
570+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
571571 k-permalink from:body -> {
572572 let b = document.body.dataset;
573573 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -385,7 +385,7 @@
385 <kbd>u</kbd>385 <kbd>u</kbd>
386 <kbd>x</kbd>386 <kbd>x</kbd>
387 </dt>387 </dt>
388-<dd>diff: split / unified / collapse all</dd>388+<dd>diff: split / unified / collapse or expand all</dd>
389 </div>389 </div>
390 <div>390 <div>
391 <dt>391 <dt>
@@ -565,9 +565,9 @@
565 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);565 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
566 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })566 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
567 };567 };
568- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');568+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
569- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');569+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
570- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);570+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
571 k-permalink from:body -> {571 k-permalink from:body -> {
572 let b = document.body.dataset;572 let b = document.body.dataset;
573 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }573 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/empty.html +4 -4
@@ -162,7 +162,7 @@
162162 <kbd>u</kbd>
163163 <kbd>x</kbd>
164164 </dt>
165-<dd>diff: split / unified / collapse all</dd>
165+<dd>diff: split / unified / collapse or expand all</dd>
166166 </div>
167167 <div>
168168 <dt>
@@ -259,9 +259,9 @@
259259 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
260260 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
261261 };
262- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
263- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
264- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
262+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
263+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
264+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
265265 k-permalink from:body -> {
266266 let b = document.body.dataset;
267267 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -162,7 +162,7 @@
162 <kbd>u</kbd>162 <kbd>u</kbd>
163 <kbd>x</kbd>163 <kbd>x</kbd>
164 </dt>164 </dt>
165-<dd>diff: split / unified / collapse all</dd>165+<dd>diff: split / unified / collapse or expand all</dd>
166 </div>166 </div>
167 <div>167 <div>
168 <dt>168 <dt>
@@ -259,9 +259,9 @@
259 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);259 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
260 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })260 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
261 };261 };
262- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');262+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
263- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');263+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
264- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);264+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
265 k-permalink from:body -> {265 k-permalink from:body -> {
266 let b = document.body.dataset;266 let b = document.body.dataset;
267 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }267 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/home.html +4 -4
@@ -192,7 +192,7 @@
192192 <kbd>u</kbd>
193193 <kbd>x</kbd>
194194 </dt>
195-<dd>diff: split / unified / collapse all</dd>
195+<dd>diff: split / unified / collapse or expand all</dd>
196196 </div>
197197 <div>
198198 <dt>
@@ -295,9 +295,9 @@
295295 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
296296 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
297297 };
298- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
299- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
300- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
298+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
299+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
300+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
301301 k-permalink from:body -> {
302302 let b = document.body.dataset;
303303 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -192,7 +192,7 @@
192 <kbd>u</kbd>192 <kbd>u</kbd>
193 <kbd>x</kbd>193 <kbd>x</kbd>
194 </dt>194 </dt>
195-<dd>diff: split / unified / collapse all</dd>195+<dd>diff: split / unified / collapse or expand all</dd>
196 </div>196 </div>
197 <div>197 <div>
198 <dt>198 <dt>
@@ -295,9 +295,9 @@
295 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);295 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
296 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })296 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
297 };297 };
298- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');298+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
299- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');299+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
300- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);300+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
301 k-permalink from:body -> {301 k-permalink from:body -> {
302 let b = document.body.dataset;302 let b = document.body.dataset;
303 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }303 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/not-found-page.html +4 -4
@@ -151,7 +151,7 @@
151151 <kbd>u</kbd>
152152 <kbd>x</kbd>
153153 </dt>
154-<dd>diff: split / unified / collapse all</dd>
154+<dd>diff: split / unified / collapse or expand all</dd>
155155 </div>
156156 <div>
157157 <dt>
@@ -242,9 +242,9 @@
242242 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
243243 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
244244 };
245- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
246- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
247- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
245+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
246+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
247+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
248248 k-permalink from:body -> {
249249 let b = document.body.dataset;
250250 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -151,7 +151,7 @@
151 <kbd>u</kbd>151 <kbd>u</kbd>
152 <kbd>x</kbd>152 <kbd>x</kbd>
153 </dt>153 </dt>
154-<dd>diff: split / unified / collapse all</dd>154+<dd>diff: split / unified / collapse or expand all</dd>
155 </div>155 </div>
156 <div>156 <div>
157 <dt>157 <dt>
@@ -242,9 +242,9 @@
242 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);242 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
243 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })243 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
244 };244 };
245- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');245+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
246- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');246+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
247- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);247+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
248 k-permalink from:body -> {248 k-permalink from:body -> {
249 let b = document.body.dataset;249 let b = document.body.dataset;
250 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }250 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/not-found.html +4 -4
@@ -151,7 +151,7 @@
151151 <kbd>u</kbd>
152152 <kbd>x</kbd>
153153 </dt>
154-<dd>diff: split / unified / collapse all</dd>
154+<dd>diff: split / unified / collapse or expand all</dd>
155155 </div>
156156 <div>
157157 <dt>
@@ -242,9 +242,9 @@
242242 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
243243 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
244244 };
245- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
246- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
247- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
245+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
246+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
247+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
248248 k-permalink from:body -> {
249249 let b = document.body.dataset;
250250 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -151,7 +151,7 @@
151 <kbd>u</kbd>151 <kbd>u</kbd>
152 <kbd>x</kbd>152 <kbd>x</kbd>
153 </dt>153 </dt>
154-<dd>diff: split / unified / collapse all</dd>154+<dd>diff: split / unified / collapse or expand all</dd>
155 </div>155 </div>
156 <div>156 <div>
157 <dt>157 <dt>
@@ -242,9 +242,9 @@
242 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);242 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
243 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })243 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
244 };244 };
245- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');245+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
246- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');246+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
247- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);247+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
248 k-permalink from:body -> {248 k-permalink from:body -> {
249 let b = document.body.dataset;249 let b = document.body.dataset;
250 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }250 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/refs.html +4 -4
@@ -276,7 +276,7 @@
276276 <kbd>u</kbd>
277277 <kbd>x</kbd>
278278 </dt>
279-<dd>diff: split / unified / collapse all</dd>
279+<dd>diff: split / unified / collapse or expand all</dd>
280280 </div>
281281 <div>
282282 <dt>
@@ -466,9 +466,9 @@
466466 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
467467 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
468468 };
469- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
470- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
471- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
469+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
470+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
471+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
472472 k-permalink from:body -> {
473473 let b = document.body.dataset;
474474 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -276,7 +276,7 @@
276 <kbd>u</kbd>276 <kbd>u</kbd>
277 <kbd>x</kbd>277 <kbd>x</kbd>
278 </dt>278 </dt>
279-<dd>diff: split / unified / collapse all</dd>279+<dd>diff: split / unified / collapse or expand all</dd>
280 </div>280 </div>
281 <div>281 <div>
282 <dt>282 <dt>
@@ -466,9 +466,9 @@
466 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);466 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
467 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })467 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
468 };468 };
469- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');469+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
470- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');470+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
471- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);471+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
472 k-permalink from:body -> {472 k-permalink from:body -> {
473 let b = document.body.dataset;473 let b = document.body.dataset;
474 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }474 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/summary.html +17 -6
@@ -127,14 +127,25 @@
127127 </dd>
128128 <dt>branch</dt>
129129 <dd>main · 2 branches</dd>
130-<dt>tag</dt>
131-<dd>v0.2.0 · AGO · 2 tags</dd>
132130 <dt>created</dt>
133131 <dd>
134132 <time datetime="STAMP">STAMP</time>
135133 </dd>
136134 </dl>
137135 </section>
136+<section aria-labelledby="r-tag">
137+<h2 id="r-tag">Tags <span class="count">2</span>
138+<a href="/demo/refs">View all</a>
139+</h2>
140+<div class="latest-tag">
141+<a class="tag-name" href="/demo/tree/v0.2.0">
142+<span class="ico i-tag" aria-hidden="true">
143+</span>v0.2.0</a>
144+<span class="pill">latest</span>
145+<time class="dim" datetime="STAMP" title="STAMP">AGO</time>
146+<p class="tag-sub dim">add main.go</p>
147+</div>
148+</section>
138149 <section aria-labelledby="r-recent">
139150 <h2 id="r-recent">Recent commits <a href="/demo/commits/main">View all</a>
140151 </h2>
@@ -281,7 +292,7 @@
281292 <kbd>u</kbd>
282293 <kbd>x</kbd>
283294 </dt>
284-<dd>diff: split / unified / collapse all</dd>
295+<dd>diff: split / unified / collapse or expand all</dd>
285296 </div>
286297 <div>
287298 <dt>
@@ -471,9 +482,9 @@
471482 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
472483 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
473484 };
474- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
475- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
476- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
485+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
486+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
487+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
477488 k-permalink from:body -> {
478489 let b = document.body.dataset;
479490 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -127,14 +127,25 @@
127 </dd>127 </dd>
128 <dt>branch</dt>128 <dt>branch</dt>
129 <dd>main · 2 branches</dd>129 <dd>main · 2 branches</dd>
130-<dt>tag</dt>
131-<dd>v0.2.0 · AGO · 2 tags</dd>
132 <dt>created</dt>130 <dt>created</dt>
133 <dd>131 <dd>
134 <time datetime="STAMP">STAMP</time>132 <time datetime="STAMP">STAMP</time>
135 </dd>133 </dd>
136 </dl>134 </dl>
137 </section>135 </section>
136+<section aria-labelledby="r-tag">
137+<h2 id="r-tag">Tags <span class="count">2</span>
138+<a href="/demo/refs">View all</a>
139+</h2>
140+<div class="latest-tag">
141+<a class="tag-name" href="/demo/tree/v0.2.0">
142+<span class="ico i-tag" aria-hidden="true">
143+</span>v0.2.0</a>
144+<span class="pill">latest</span>
145+<time class="dim" datetime="STAMP" title="STAMP">AGO</time>
146+<p class="tag-sub dim">add main.go</p>
147+</div>
148+</section>
138 <section aria-labelledby="r-recent">149 <section aria-labelledby="r-recent">
139 <h2 id="r-recent">Recent commits <a href="/demo/commits/main">View all</a>150 <h2 id="r-recent">Recent commits <a href="/demo/commits/main">View all</a>
140 </h2>151 </h2>
@@ -281,7 +292,7 @@
281 <kbd>u</kbd>292 <kbd>u</kbd>
282 <kbd>x</kbd>293 <kbd>x</kbd>
283 </dt>294 </dt>
284-<dd>diff: split / unified / collapse all</dd>295+<dd>diff: split / unified / collapse or expand all</dd>
285 </div>296 </div>
286 <div>297 <div>
287 <dt>298 <dt>
@@ -471,9 +482,9 @@
471 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);482 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
472 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })483 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
473 };484 };
474- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');485+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
475- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');486+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
476- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);487+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
477 k-permalink from:body -> {488 k-permalink from:body -> {
478 let b = document.body.dataset;489 let b = document.body.dataset;
479 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }490 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/tree-nested.html +4 -4
@@ -221,7 +221,7 @@
221221 <kbd>u</kbd>
222222 <kbd>x</kbd>
223223 </dt>
224-<dd>diff: split / unified / collapse all</dd>
224+<dd>diff: split / unified / collapse or expand all</dd>
225225 </div>
226226 <div>
227227 <dt>
@@ -401,9 +401,9 @@
401401 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
402402 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
403403 };
404- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
405- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
406- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
404+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
405+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
406+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
407407 k-permalink from:body -> {
408408 let b = document.body.dataset;
409409 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -221,7 +221,7 @@
221 <kbd>u</kbd>221 <kbd>u</kbd>
222 <kbd>x</kbd>222 <kbd>x</kbd>
223 </dt>223 </dt>
224-<dd>diff: split / unified / collapse all</dd>224+<dd>diff: split / unified / collapse or expand all</dd>
225 </div>225 </div>
226 <div>226 <div>
227 <dt>227 <dt>
@@ -401,9 +401,9 @@
401 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);401 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
402 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })402 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
403 };403 };
404- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');404+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
405- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');405+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
406- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);406+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
407 k-permalink from:body -> {407 k-permalink from:body -> {
408 let b = document.body.dataset;408 let b = document.body.dataset;
409 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }409 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/tree-root.html +4 -4
@@ -448,7 +448,7 @@
448448 <kbd>u</kbd>
449449 <kbd>x</kbd>
450450 </dt>
451-<dd>diff: split / unified / collapse all</dd>
451+<dd>diff: split / unified / collapse or expand all</dd>
452452 </div>
453453 <div>
454454 <dt>
@@ -700,9 +700,9 @@
700700 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
701701 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
702702 };
703- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
704- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
705- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
703+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
704+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
705+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
706706 k-permalink from:body -> {
707707 let b = document.body.dataset;
708708 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -448,7 +448,7 @@
448 <kbd>u</kbd>448 <kbd>u</kbd>
449 <kbd>x</kbd>449 <kbd>x</kbd>
450 </dt>450 </dt>
451-<dd>diff: split / unified / collapse all</dd>451+<dd>diff: split / unified / collapse or expand all</dd>
452 </div>452 </div>
453 <div>453 <div>
454 <dt>454 <dt>
@@ -700,9 +700,9 @@
700 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);700 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
701 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })701 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
702 };702 };
703- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');703+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
704- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');704+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
705- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);705+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
706 k-permalink from:body -> {706 k-permalink from:body -> {
707 let b = document.body.dataset;707 let b = document.body.dataset;
708 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }708 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/tree-slash-ref.html +4 -4
@@ -218,7 +218,7 @@
218218 <kbd>u</kbd>
219219 <kbd>x</kbd>
220220 </dt>
221-<dd>diff: split / unified / collapse all</dd>
221+<dd>diff: split / unified / collapse or expand all</dd>
222222 </div>
223223 <div>
224224 <dt>
@@ -398,9 +398,9 @@
398398 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
399399 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
400400 };
401- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
402- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
403- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
401+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
402+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
403+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
404404 k-permalink from:body -> {
405405 let b = document.body.dataset;
406406 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -218,7 +218,7 @@
218 <kbd>u</kbd>218 <kbd>u</kbd>
219 <kbd>x</kbd>219 <kbd>x</kbd>
220 </dt>220 </dt>
221-<dd>diff: split / unified / collapse all</dd>221+<dd>diff: split / unified / collapse or expand all</dd>
222 </div>222 </div>
223 <div>223 <div>
224 <dt>224 <dt>
@@ -398,9 +398,9 @@
398 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);398 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
399 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })399 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
400 };400 };
401- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');401+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
402- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');402+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
403- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);403+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
404 k-permalink from:body -> {404 k-permalink from:body -> {
405 let b = document.body.dataset;405 let b = document.body.dataset;
406 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }406 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M cmd/ermite/testdata/web/tree-subdir.html +4 -4
@@ -243,7 +243,7 @@
243243 <kbd>u</kbd>
244244 <kbd>x</kbd>
245245 </dt>
246-<dd>diff: split / unified / collapse all</dd>
246+<dd>diff: split / unified / collapse or expand all</dd>
247247 </div>
248248 <div>
249249 <dt>
@@ -429,9 +429,9 @@
429429 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
430430 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
431431 };
432- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');
433- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');
434- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);
432+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
433+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
434+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
435435 k-permalink from:body -> {
436436 let b = document.body.dataset;
437437 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
@@ -243,7 +243,7 @@
243 <kbd>u</kbd>243 <kbd>u</kbd>
244 <kbd>x</kbd>244 <kbd>x</kbd>
245 </dt>245 </dt>
246-<dd>diff: split / unified / collapse all</dd>246+<dd>diff: split / unified / collapse or expand all</dd>
247 </div>247 </div>
248 <div>248 <div>
249 <dt>249 <dt>
@@ -429,9 +429,9 @@
429 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);429 let i = ns.findIndex(n => n.getBoundingClientRect().top > innerHeight / 3);
430 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })430 let n = ns[i < 0 ? 0 : i]; n.closest('details') && (n.closest('details').open = true); n.scrollIntoView({ block: 'center' })
431 };431 };
432- k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split');432+ k-split from:body -> q('[data-diff]').attr['data-diff'] = 'split'; sessionStorage.setItem('diff', 'split'); q('[data-file]').arr().forEach(d => d.open = true);
433- k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified');433+ k-unified from:body -> q('[data-diff]').attr['data-diff'] = 'unified'; sessionStorage.setItem('diff', 'unified'); q('[data-file]').arr().forEach(d => d.open = true);
434- k-collapse from:body -> q('[data-file]').arr().forEach(d => d.open = false);434+ k-collapse from:body -> { let fs = q('[data-file]').arr(), open = !fs.some(d => d.open); fs.forEach(d => d.open = open) };
435 k-permalink from:body -> {435 k-permalink from:body -> {
436 let b = document.body.dataset;436 let b = document.body.dataset;
437 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }437 if (!b.sha) { b.flash = 'no ref to pin on this page'; await timeout('2s'); b.flash = ''; return }
M internal/adapters/web/pages.templ +15 -0
@@ -59,6 +59,21 @@ templ summaryRail(p SummaryPage) {
5959 </section>
6060 <section aria-labelledby="r-facts"><h2 id="r-facts">About</h2>@Facts(p.Facts)
6161 </section>
62+ if p.Latest != nil {
63+ <section aria-labelledby="r-tag">
64+ <h2 id="r-tag">Tags <span class="count">{ itoa(p.Latest.Count) }</span> <a href={ templ.SafeURL(p.Latest.RefsHref) }>View all</a></h2>
65+ if p.Latest.Name != "" {
66+ <div class="latest-tag">
67+ <a class="tag-name" href={ templ.SafeURL(p.Latest.Href) }><span class="ico i-tag" aria-hidden="true"></span>{ p.Latest.Name }</a>
68+ <span class="pill">latest</span>
69+ <time class="dim" datetime={ p.Latest.WhenAt } title={ p.Latest.WhenAt }>{ p.Latest.When }</time>
70+ if p.Latest.Subject != "" {
71+ <p class="tag-sub dim">{ p.Latest.Subject }</p>
72+ }
73+ </div>
74+ }
75+ </section>
76+ }
6277 if len(p.Recent) > 0 {
6378 <section aria-labelledby="r-recent"><h2 id="r-recent">Recent commits <a href={ templ.SafeURL(p.AllHref) }>View all</a></h2>@CommitRows(p.Recent)
6479 </section>
@@ -59,6 +59,21 @@ templ summaryRail(p SummaryPage) {
59 </section>59 </section>
60 <section aria-labelledby="r-facts"><h2 id="r-facts">About</h2>@Facts(p.Facts)60 <section aria-labelledby="r-facts"><h2 id="r-facts">About</h2>@Facts(p.Facts)
61 </section>61 </section>
62+ if p.Latest != nil {
63+ <section aria-labelledby="r-tag">
64+ <h2 id="r-tag">Tags <span class="count">{ itoa(p.Latest.Count) }</span> <a href={ templ.SafeURL(p.Latest.RefsHref) }>View all</a></h2>
65+ if p.Latest.Name != "" {
66+ <div class="latest-tag">
67+ <a class="tag-name" href={ templ.SafeURL(p.Latest.Href) }><span class="ico i-tag" aria-hidden="true"></span>{ p.Latest.Name }</a>
68+ <span class="pill">latest</span>
69+ <time class="dim" datetime={ p.Latest.WhenAt } title={ p.Latest.WhenAt }>{ p.Latest.When }</time>
70+ if p.Latest.Subject != "" {
71+ <p class="tag-sub dim">{ p.Latest.Subject }</p>
72+ }
73+ </div>
74+ }
75+ </section>
76+ }
62 if len(p.Recent) > 0 {77 if len(p.Recent) > 0 {
63 <section aria-labelledby="r-recent"><h2 id="r-recent">Recent commits <a href={ templ.SafeURL(p.AllHref) }>View all</a></h2>@CommitRows(p.Recent)78 <section aria-labelledby="r-recent"><h2 id="r-recent">Recent commits <a href={ templ.SafeURL(p.AllHref) }>View all</a></h2>@CommitRows(p.Recent)
64 </section>79 </section>
M internal/adapters/web/pages_templ.go +281 -151 generated file: collapsed
M internal/adapters/web/static/css/20-shell.css +11 -0
@@ -20,6 +20,7 @@
2020 .i-branch { --i: url(icons/branch.svg); }
2121 .i-search { --i: url(icons/search.svg); }
2222 .i-file { --i: url(icons/file.svg); }
23+ .i-tag { --i: url(icons/tag.svg); }
2324
2425 .pill {
2526 display: inline-flex; align-items: center; height: 20px; padding: 0 var(--s2); margin-left: var(--s1);
@@ -123,6 +124,16 @@
123124 .rail section > :not(h2) { margin: var(--s3) var(--s4); }
124125 .rail section > .list { margin: var(--s2) 0; }
125126 .rail section > p + p { margin-top: calc(-1 * var(--s2)); }
127+ .rail h2 .count + a { margin-left: auto; }
128+
129+ /* newest tag in the summary rail */
130+ .latest-tag {
131+ display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); font-size: var(--fs-s);
132+ & .tag-name { display: inline-flex; align-items: center; gap: 6px; font: 700 var(--fs) / 20px var(--mono); color: var(--ink-hi); text-decoration: none; }
133+ & .tag-name:hover { text-decoration: underline; }
134+ & time { margin-left: auto; }
135+ & .tag-sub { flex-basis: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
136+ }
126137
127138 .kv {
128139 display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--s4); row-gap: var(--s1);
@@ -20,6 +20,7 @@
20 .i-branch { --i: url(icons/branch.svg); }20 .i-branch { --i: url(icons/branch.svg); }
21 .i-search { --i: url(icons/search.svg); }21 .i-search { --i: url(icons/search.svg); }
22 .i-file { --i: url(icons/file.svg); }22 .i-file { --i: url(icons/file.svg); }
23+ .i-tag { --i: url(icons/tag.svg); }
23 24
24 .pill {25 .pill {
25 display: inline-flex; align-items: center; height: 20px; padding: 0 var(--s2); margin-left: var(--s1);26 display: inline-flex; align-items: center; height: 20px; padding: 0 var(--s2); margin-left: var(--s1);
@@ -123,6 +124,16 @@
123 .rail section > :not(h2) { margin: var(--s3) var(--s4); }124 .rail section > :not(h2) { margin: var(--s3) var(--s4); }
124 .rail section > .list { margin: var(--s2) 0; }125 .rail section > .list { margin: var(--s2) 0; }
125 .rail section > p + p { margin-top: calc(-1 * var(--s2)); }126 .rail section > p + p { margin-top: calc(-1 * var(--s2)); }
127+ .rail h2 .count + a { margin-left: auto; }
128+
129+ /* newest tag in the summary rail */
130+ .latest-tag {
131+ display: flex; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s2); font-size: var(--fs-s);
132+ & .tag-name { display: inline-flex; align-items: center; gap: 6px; font: 700 var(--fs) / 20px var(--mono); color: var(--ink-hi); text-decoration: none; }
133+ & .tag-name:hover { text-decoration: underline; }
134+ & time { margin-left: auto; }
135+ & .tag-sub { flex-basis: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
136+ }
126 137
127 .kv {138 .kv {
128 display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--s4); row-gap: var(--s1);139 display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: var(--s4); row-gap: var(--s1);
A internal/adapters/web/static/icons/tag.svg +1 -0
@@ -0,0 +1,1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 2.5v5l6.5 6.5 5-5L7 2.5z"/><circle cx="5" cy="5.5" r="1"/></svg>
@@ -0,0 +1,1 @@
1+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none" stroke="#000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M2 2.5v5l6.5 6.5 5-5L7 2.5z"/><circle cx="5" cy="5.5" r="1"/></svg>
M internal/adapters/web/summary.go +20 -12
@@ -61,7 +61,8 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
6161 p.Recent = append(p.Recent, row)
6262 }
6363
64- p.Facts = s.facts(ctx, d, now)
64+ p.Facts = s.facts(d, now)
65+ p.Latest = s.latest(ctx, d)
6566
6667 if rd, err := s.readme(ctx, d, ref); err != nil {
6768 return nil, 0, err
@@ -78,24 +79,31 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
7879 }
7980
8081 // maxTagDates is the most tags whose commit dates are read to name the
81-// latest one; with more, the fact shows the count only.
82+// latest one; with more, the rail shows the count only.
8283 const maxTagDates = 20
8384
85+// latest is the newest tag of the repository for the rail, nil without tags.
86+func (s *Server) latest(ctx context.Context, d *repoData) *LatestTag {
87+ n := len(d.tags)
88+ if n == 0 {
89+ return nil
90+ }
91+ lt := &LatestTag{Count: n, RefsHref: repoHref(d.name(), "refs")}
92+ if n <= maxTagDates {
93+ if name, c := latestTag(d.tags, s.commitsOf(ctx, d.name(), d.tags)); name != "" {
94+ lt.Name, lt.Href = name, repoHref(d.name(), "tree", name)
95+ lt.Subject, lt.When, lt.WhenAt = c.subject, c.when, c.whenAt
96+ }
97+ }
98+ return lt
99+}
100+
84101 // facts are the key/value rows of the rail.
85-func (s *Server) facts(ctx context.Context, d *repoData, now time.Time) []Fact {
102+func (s *Server) facts(d *repoData, now time.Time) []Fact {
86103 fs := []Fact{
87104 lastPushFact(d.repo, now),
88105 {Key: "branch", Val: d.head.GetShortName() + " · " + plural(len(d.branches), "branch", "branches")},
89106 }
90- if n := len(d.tags); n > 0 {
91- val := plural(n, "tag", "tags")
92- if n <= maxTagDates {
93- if name, c := latestTag(d.tags, s.commitsOf(ctx, d.name(), d.tags)); name != "" {
94- val = name + " · " + c.when + " · " + val
95- }
96- }
97- fs = append(fs, Fact{Key: "tag", Val: val})
98- }
99107 if c := d.repo.GetCreatedAt(); c.IsValid() {
100108 fs = append(fs, Fact{Key: "created", Val: absolute(c.AsTime()), Time: rfc3339(c.AsTime())})
101109 }
@@ -61,7 +61,8 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
61 p.Recent = append(p.Recent, row)61 p.Recent = append(p.Recent, row)
62 }62 }
63 63
64- p.Facts = s.facts(ctx, d, now)64+ p.Facts = s.facts(d, now)
65+ p.Latest = s.latest(ctx, d)
65 66
66 if rd, err := s.readme(ctx, d, ref); err != nil {67 if rd, err := s.readme(ctx, d, ref); err != nil {
67 return nil, 0, err68 return nil, 0, err
@@ -78,24 +79,31 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
78 }79 }
79 80
80 // maxTagDates is the most tags whose commit dates are read to name the81 // maxTagDates is the most tags whose commit dates are read to name the
81-// latest one; with more, the fact shows the count only.82+// latest one; with more, the rail shows the count only.
82 const maxTagDates = 2083 const maxTagDates = 20
83 84
85+// latest is the newest tag of the repository for the rail, nil without tags.
86+func (s *Server) latest(ctx context.Context, d *repoData) *LatestTag {
87+ n := len(d.tags)
88+ if n == 0 {
89+ return nil
90+ }
91+ lt := &LatestTag{Count: n, RefsHref: repoHref(d.name(), "refs")}
92+ if n <= maxTagDates {
93+ if name, c := latestTag(d.tags, s.commitsOf(ctx, d.name(), d.tags)); name != "" {
94+ lt.Name, lt.Href = name, repoHref(d.name(), "tree", name)
95+ lt.Subject, lt.When, lt.WhenAt = c.subject, c.when, c.whenAt
96+ }
97+ }
98+ return lt
99+}
100+
84 // facts are the key/value rows of the rail.101 // facts are the key/value rows of the rail.
85-func (s *Server) facts(ctx context.Context, d *repoData, now time.Time) []Fact {102+func (s *Server) facts(d *repoData, now time.Time) []Fact {
86 fs := []Fact{103 fs := []Fact{
87 lastPushFact(d.repo, now),104 lastPushFact(d.repo, now),
88 {Key: "branch", Val: d.head.GetShortName() + " · " + plural(len(d.branches), "branch", "branches")},105 {Key: "branch", Val: d.head.GetShortName() + " · " + plural(len(d.branches), "branch", "branches")},
89 }106 }
90- if n := len(d.tags); n > 0 {
91- val := plural(n, "tag", "tags")
92- if n <= maxTagDates {
93- if name, c := latestTag(d.tags, s.commitsOf(ctx, d.name(), d.tags)); name != "" {
94- val = name + " · " + c.when + " · " + val
95- }
96- }
97- fs = append(fs, Fact{Key: "tag", Val: val})
98- }
99 if c := d.repo.GetCreatedAt(); c.IsValid() {107 if c := d.repo.GetCreatedAt(); c.IsValid() {
100 fs = append(fs, Fact{Key: "created", Val: absolute(c.AsTime()), Time: rfc3339(c.AsTime())})108 fs = append(fs, Fact{Key: "created", Val: absolute(c.AsTime()), Time: rfc3339(c.AsTime())})
101 }109 }
M internal/adapters/web/view.go +11 -0
@@ -141,6 +141,17 @@ type SummaryPage struct {
141141 Readme *Readme
142142 Outline []Heading
143143 AllHref string
144+ // Latest is the newest tag, nil when the repository has none.
145+ Latest *LatestTag
146+}
147+
148+// LatestTag is the newest tag of a repository, shown in the summary rail.
149+// Name is empty when there are too many tags to date them; Count and
150+// RefsHref still lead to the full list.
151+type LatestTag struct {
152+ Name, Href, Subject, When, WhenAt string
153+ Count int
154+ RefsHref string
144155 }
145156
146157 // RefRow is a branch or a tag in the refs table.
@@ -141,6 +141,17 @@ type SummaryPage struct {
141 Readme *Readme141 Readme *Readme
142 Outline []Heading142 Outline []Heading
143 AllHref string143 AllHref string
144+ // Latest is the newest tag, nil when the repository has none.
145+ Latest *LatestTag
146+}
147+
148+// LatestTag is the newest tag of a repository, shown in the summary rail.
149+// Name is empty when there are too many tags to date them; Count and
150+// RefsHref still lead to the full list.
151+type LatestTag struct {
152+ Name, Href, Subject, When, WhenAt string
153+ Count int
154+ RefsHref string
144 }155 }
145 156
146 // RefRow is a branch or a tag in the refs table.157 // RefRow is a branch or a tag in the refs table.

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