Merge 53e25a360b92a093115c2051beb9c56d1afe48e6 into 5dbd27fc45163a9fccc5f6cf45c55ed6935ad7d3
4 files · +94 -83
M internal/adapters/web/mr.templ +7 -6
| @@ -467,12 +467,13 @@ templ mrBar(p MRPage) { | |||
| 467 | 467 | <div class="diff-tools mr-bar" role="group" aria-label="Diff view and merge"> | |
| 468 | 468 | <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span> | |
| 469 | 469 | @diffViewMenu(p.DiffView) | |
| 470 | - | <span class="grow"></span> | |
| 471 | - | @MRChecksSum(p.ChecksSum) | |
| 472 | - | if p.Box != nil { | |
| 473 | - | @mergeMenu(p) | |
| 474 | - | } | |
| 475 | - | @moreMenu(p) | |
| 470 | + | <div class="mr-acts"> | |
| 471 | + | @MRChecksSum(p.ChecksSum) | |
| 472 | + | if p.Box != nil { | |
| 473 | + | @mergeMenu(p) | |
| 474 | + | } | |
| 475 | + | @moreMenu(p) | |
| 476 | + | </div> | |
| 476 | 477 | </div> | |
| 477 | 478 | } | |
| 478 | 479 | ||
| @@ -467,12 +467,13 @@ templ mrBar(p MRPage) { | |||||
| 467 | <div class="diff-tools mr-bar" role="group" aria-label="Diff view and merge"> | 467 | <div class="diff-tools mr-bar" role="group" aria-label="Diff view and merge"> | ||
| 468 | <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span> | 468 | <span class="dim">{ plural(p.NFiles, "file", "files") } · <span class="plus">+{ itoa(p.Adds) }</span> <span class="minus">-{ itoa(p.Dels) }</span></span> | ||
| 469 | @diffViewMenu(p.DiffView) | 469 | @diffViewMenu(p.DiffView) | ||
| 470 | - | <span class="grow"></span> | 470 | + | <div class="mr-acts"> |
| 471 | - | @MRChecksSum(p.ChecksSum) | 471 | + | @MRChecksSum(p.ChecksSum) |
| 472 | - | if p.Box != nil { | 472 | + | if p.Box != nil { |
| 473 | - | @mergeMenu(p) | 473 | + | @mergeMenu(p) |
| 474 | - | } | 474 | + | } |
| 475 | - | @moreMenu(p) | 475 | + | @moreMenu(p) |
| 476 | + | </div> | |||
| 476 | </div> | 477 | </div> | ||
| 477 | } | 478 | } | ||
| 478 | 479 | ||||
M internal/adapters/web/mr_templ.go +71 -71 generated file: collapsed
M internal/adapters/web/static/css/60-mr.css +4 -6
| @@ -61,14 +61,12 @@ | |||
| 61 | 61 | ||
| 62 | 62 | /* the bar above the diff of a merge request: the diff tools, the checks and | |
| 63 | 63 | the menus of the owner, on one line that follows the review */ | |
| 64 | - | /* on a narrow screen the bar takes two lines: the diff tools, then the | |
| 65 | - | checks and the menus; the file headers stick under both */ | |
| 66 | - | @media (max-width: 860px) { | |
| 67 | - | body:has(.mr-bar) { --bar-h: 96px; } | |
| 68 | - | .mr-bar .grow { flex-basis: 100%; height: 0; } | |
| 69 | - | } | |
| 64 | + | /* the checks and the menus stay on the right, also when they wrap under the | |
| 65 | + | diff tools; mr.js measures the height of the bar (--mr-bar-h) */ | |
| 66 | + | body:has(.mr-bar) { --bar-h: var(--mr-bar-h, 48px); } | |
| 70 | 67 | .mr-bar { | |
| 71 | 68 | gap: var(--s2); | |
| 69 | + | & .mr-acts { display: flex; gap: var(--s2); align-items: center; margin-left: auto; } | |
| 72 | 70 | & .mr-ck { margin-left: 0; height: 32px; padding: 0 var(--s2); border-radius: var(--r-s); text-decoration: none; color: var(--ink-dim); font-size: var(--fs-s); } | |
| 73 | 71 | & .mr-ck:hover { background: var(--bg-raised); color: var(--ink-hi); } | |
| 74 | 72 | } | |
| @@ -61,14 +61,12 @@ | |||||
| 61 | 61 | ||||
| 62 | /* the bar above the diff of a merge request: the diff tools, the checks and | 62 | /* the bar above the diff of a merge request: the diff tools, the checks and | ||
| 63 | the menus of the owner, on one line that follows the review */ | 63 | the menus of the owner, on one line that follows the review */ | ||
| 64 | - | /* on a narrow screen the bar takes two lines: the diff tools, then the | 64 | + | /* the checks and the menus stay on the right, also when they wrap under the |
| 65 | - | checks and the menus; the file headers stick under both */ | 65 | + | diff tools; mr.js measures the height of the bar (--mr-bar-h) */ |
| 66 | - | @media (max-width: 860px) { | 66 | + | body:has(.mr-bar) { --bar-h: var(--mr-bar-h, 48px); } |
| 67 | - | body:has(.mr-bar) { --bar-h: 96px; } | |||
| 68 | - | .mr-bar .grow { flex-basis: 100%; height: 0; } | |||
| 69 | - | } | |||
| 70 | .mr-bar { | 67 | .mr-bar { | ||
| 71 | gap: var(--s2); | 68 | gap: var(--s2); | ||
| 69 | + | & .mr-acts { display: flex; gap: var(--s2); align-items: center; margin-left: auto; } | |||
| 72 | & .mr-ck { margin-left: 0; height: 32px; padding: 0 var(--s2); border-radius: var(--r-s); text-decoration: none; color: var(--ink-dim); font-size: var(--fs-s); } | 70 | & .mr-ck { margin-left: 0; height: 32px; padding: 0 var(--s2); border-radius: var(--r-s); text-decoration: none; color: var(--ink-dim); font-size: var(--fs-s); } | ||
| 73 | & .mr-ck:hover { background: var(--bg-raised); color: var(--ink-hi); } | 71 | & .mr-ck:hover { background: var(--bg-raised); color: var(--ink-hi); } | ||
| 74 | } | 72 | } | ||
M internal/adapters/web/static/js/mr.js +12 -0
| @@ -81,6 +81,16 @@ window.ermiteMR = (() => { | |||
| 81 | 81 | const $$ = (sel, root = document) => [...root.querySelectorAll(sel)]; | |
| 82 | 82 | let refreshTouchComments = () => {}; | |
| 83 | 83 | let revealGen = 0; // the last reveal wins the scroll | |
| 84 | + | // The bar wraps only when its items do not fit: the file headers stick | |
| 85 | + | // under its measured height (--mr-bar-h), not under a guessed one. | |
| 86 | + | let barObserver = null; | |
| 87 | + | function watchBar() { | |
| 88 | + | barObserver?.disconnect(); | |
| 89 | + | const bar = $('.mr-bar'); | |
| 90 | + | if (!bar || typeof ResizeObserver !== 'function') return; | |
| 91 | + | barObserver = new ResizeObserver(() => document.body.style.setProperty('--mr-bar-h', bar.offsetHeight + 'px')); | |
| 92 | + | barObserver.observe(bar); | |
| 93 | + | } | |
| 84 | 94 | const load = (ph) => window.ermiteLazy.load(ph); | |
| 85 | 95 | window.ermiteLazy.onSwap(() => refreshTouchComments()); | |
| 86 | 96 | ||
| @@ -536,6 +546,7 @@ window.ermiteMR = (() => { | |||
| 536 | 546 | if (oldSHA && newSHA) oldSHA.textContent = newSHA.textContent; | |
| 537 | 547 | page = null; | |
| 538 | 548 | panels(openPanel); | |
| 549 | + | watchBar(); | |
| 539 | 550 | foldFiles(!!folded); | |
| 540 | 551 | if ($('.rail')) $('.rail').scrollTop = railY; | |
| 541 | 552 | if ($('.r-files .files')) $('.r-files .files').scrollTop = filesY; | |
| @@ -593,6 +604,7 @@ window.ermiteMR = (() => { | |||
| 593 | 604 | headRefresh.resume(); | |
| 594 | 605 | }); | |
| 595 | 606 | panels(); | |
| 607 | + | watchBar(); | |
| 596 | 608 | // Large diffs may contain only the split table. Ask the existing server | |
| 597 | 609 | // route for unified rather than writing the saved session preference. | |
| 598 | 610 | const narrow = window.matchMedia?.('(max-width: 720px)'); | |
| @@ -81,6 +81,16 @@ window.ermiteMR = (() => { | |||||
| 81 | const $$ = (sel, root = document) => [...root.querySelectorAll(sel)]; | 81 | const $$ = (sel, root = document) => [...root.querySelectorAll(sel)]; | ||
| 82 | let refreshTouchComments = () => {}; | 82 | let refreshTouchComments = () => {}; | ||
| 83 | let revealGen = 0; // the last reveal wins the scroll | 83 | let revealGen = 0; // the last reveal wins the scroll | ||
| 84 | + | // The bar wraps only when its items do not fit: the file headers stick | |||
| 85 | + | // under its measured height (--mr-bar-h), not under a guessed one. | |||
| 86 | + | let barObserver = null; | |||
| 87 | + | function watchBar() { | |||
| 88 | + | barObserver?.disconnect(); | |||
| 89 | + | const bar = $('.mr-bar'); | |||
| 90 | + | if (!bar || typeof ResizeObserver !== 'function') return; | |||
| 91 | + | barObserver = new ResizeObserver(() => document.body.style.setProperty('--mr-bar-h', bar.offsetHeight + 'px')); | |||
| 92 | + | barObserver.observe(bar); | |||
| 93 | + | } | |||
| 84 | const load = (ph) => window.ermiteLazy.load(ph); | 94 | const load = (ph) => window.ermiteLazy.load(ph); | ||
| 85 | window.ermiteLazy.onSwap(() => refreshTouchComments()); | 95 | window.ermiteLazy.onSwap(() => refreshTouchComments()); | ||
| 86 | 96 | ||||
| @@ -536,6 +546,7 @@ window.ermiteMR = (() => { | |||||
| 536 | if (oldSHA && newSHA) oldSHA.textContent = newSHA.textContent; | 546 | if (oldSHA && newSHA) oldSHA.textContent = newSHA.textContent; | ||
| 537 | page = null; | 547 | page = null; | ||
| 538 | panels(openPanel); | 548 | panels(openPanel); | ||
| 549 | + | watchBar(); | |||
| 539 | foldFiles(!!folded); | 550 | foldFiles(!!folded); | ||
| 540 | if ($('.rail')) $('.rail').scrollTop = railY; | 551 | if ($('.rail')) $('.rail').scrollTop = railY; | ||
| 541 | if ($('.r-files .files')) $('.r-files .files').scrollTop = filesY; | 552 | if ($('.r-files .files')) $('.r-files .files').scrollTop = filesY; | ||
| @@ -593,6 +604,7 @@ window.ermiteMR = (() => { | |||||
| 593 | headRefresh.resume(); | 604 | headRefresh.resume(); | ||
| 594 | }); | 605 | }); | ||
| 595 | panels(); | 606 | panels(); | ||
| 607 | + | watchBar(); | |||
| 596 | // Large diffs may contain only the split table. Ask the existing server | 608 | // Large diffs may contain only the split table. Ask the existing server | ||
| 597 | // route for unified rather than writing the saved session preference. | 609 | // route for unified rather than writing the saved session preference. | ||
| 598 | const narrow = window.matchMedia?.('(max-width: 720px)'); | 610 | const narrow = window.matchMedia?.('(max-width: 720px)'); | ||