detached fe19220

#13 feat(web): rework the merge request view panels and mobile layout

state
merged · fe19220 · fast-forward
branch
feat/mr-view-ux → main
opened
bnema ·
updated

Summary

  • Commits and checks move from the rail into a collapsed, tabbed footer of the main card. Checks stay live; the checks summary link opens the footer on its tab.
  • Merge settings and the signing note move to the More (⋯) menu. The merge menu drops the head and target rows.
  • Mobile (≤720px): edge-to-edge layout, unified diff without changing the saved preference, bottom-sheet menus.
  • Touch: tall line-number targets and a tap-to-select line range for comments.

Notes

  • The commit count shows N+ when the list is truncated.
  • Not checked yet: the rendering on a real phone.

Checks

  • go build, go vet and go test ./... pass.
  • node --test passes 166 JS tests.
  • templ generate -check reports no diff.
CommitSubjectAuthorDateSignatureStatus
fe19220test(web): run the MR push subtests in parallelbriceGPG Verified
a01252afix(web): keep the open MR footer panel through an in-place refreshbriceGPG Verified
6b81fd4fix(web): show MR footer panels without scripts and style the tabs as buttonsbriceGPG Verified
f525ac6feat(web): turn the MR footer into toggle tabs on a darker backgroundbriceGPG Verified
89e7d82fix(web): stop the checks label observer from looping with hx-livebriceGPG Verified
a034b8cfix(web): treat a chosen merge mode as a draft that blocks the refreshbriceGPG Verified
18da841fix(web): handle the live stream reader cancellation at its sourcebriceGPG Verified
e688ec5fix(web): keep MR form drafts and the live stream across refreshesbriceGPG Verified
cff892bfix(web): keep the MR refresh anchored, draft safe and leanbriceGPG Verified
e97c08efeat(web): refresh the MR view in place when its source branch movesbriceGPG Verified
b46e8c1fix(web): resolve MR view review findingsbriceGPG Verified
52bc7c7style(web): fit the MR footer to the mobile layoutbriceGPG Verified
97c558fMerge branch 'feat/mr-view-ux-mobile' into feat/mr-view-uxbriceGPG Verified
966bf44Merge branch 'feat/mr-view-ux-panels' into feat/mr-view-uxbriceGPG Verified
d92c6dcfeat(web): make the MR view edge to edge and touch friendly on mobilebriceGPG Verified
42e48d0feat(web): move MR commits and checks into a tabbed card footerbriceGPG Verified

Checks 8

40 files · +2546 -1249
8/8
M docs/merge-requests.md +29 -0
M internal/adapters/web/actions.templ +2 -2
M internal/adapters/web/actions_templ.go +2 -2
M internal/adapters/web/checks.go +1 -1
M internal/adapters/web/diff.templ +6 -2
M internal/adapters/web/diff_templ.go +190 -184
M internal/adapters/web/events.go +13 -3
M internal/adapters/web/events_test.go +50 -0
M internal/adapters/web/fragments.go +2 -7
M internal/adapters/web/fragments.templ +1 -0
M internal/adapters/web/fragments_templ.go +1 -1
M internal/adapters/web/fragments_test.go +22 -0
M internal/adapters/web/jstest/a11y.test.mjs +23 -0
M internal/adapters/web/jstest/live.test.mjs +43 -2
M internal/adapters/web/jstest/mr.test.mjs +430 -0
M internal/adapters/web/mr.go +3 -0
M internal/adapters/web/mr.templ +141 -112
M internal/adapters/web/mr_templ.go +938 -894
M internal/adapters/web/mr_test.go +67 -0
M internal/adapters/web/signature_test.go +1 -1
M internal/adapters/web/static/css/60-mr.css +34 -2
M internal/adapters/web/static/css/80-live.css +5 -0
A internal/adapters/web/static/css/85-mobile.css +53 -0
M internal/adapters/web/static/js/ermite.js +36 -2
M internal/adapters/web/static/js/mr.js +408 -2
M internal/app/testdata/web/commit-big.html +3 -3
@@ -127,12 +127,12 @@
127127 </span>
128128 <details class="menu vmenu">
129129 <summary class="btn" aria-label="Diff view">
130-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
130+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
131131 </summary>
132132 <div class="menu-pop" role="group" aria-label="Diff view">
133-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
133+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
134134 </a>
135-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
135+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
136136 </a>
137137 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
138138 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -127,12 +127,12 @@
127 </span>127 </span>
128 <details class="menu vmenu">128 <details class="menu vmenu">
129 <summary class="btn" aria-label="Diff view">129 <summary class="btn" aria-label="Diff view">
130-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>130+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
131 </summary>131 </summary>
132 <div class="menu-pop" role="group" aria-label="Diff view">132 <div class="menu-pop" role="group" aria-label="Diff view">
133-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>133+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
134 </a>134 </a>
135-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>135+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
136 </a>136 </a>
137 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">137 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
138 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>138 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-binary.html +3 -3
@@ -123,12 +123,12 @@
123123 </span>
124124 <details class="menu vmenu">
125125 <summary class="btn" aria-label="Diff view">
126-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
126+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
127127 </summary>
128128 <div class="menu-pop" role="group" aria-label="Diff view">
129-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
129+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
130130 </a>
131-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
131+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
132132 </a>
133133 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
134134 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -123,12 +123,12 @@
123 </span>123 </span>
124 <details class="menu vmenu">124 <details class="menu vmenu">
125 <summary class="btn" aria-label="Diff view">125 <summary class="btn" aria-label="Diff view">
126-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>126+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
127 </summary>127 </summary>
128 <div class="menu-pop" role="group" aria-label="Diff view">128 <div class="menu-pop" role="group" aria-label="Diff view">
129-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>129+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
130 </a>130 </a>
131-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>131+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
132 </a>132 </a>
133 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">133 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
134 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>134 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-merge.html +3 -3
@@ -122,12 +122,12 @@
122122 </span>
123123 <details class="menu vmenu">
124124 <summary class="btn" aria-label="Diff view">
125-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
125+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
126126 </summary>
127127 <div class="menu-pop" role="group" aria-label="Diff view">
128-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
128+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
129129 </a>
130-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
130+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
131131 </a>
132132 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
133133 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -122,12 +122,12 @@
122 </span>122 </span>
123 <details class="menu vmenu">123 <details class="menu vmenu">
124 <summary class="btn" aria-label="Diff view">124 <summary class="btn" aria-label="Diff view">
125-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>125+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
126 </summary>126 </summary>
127 <div class="menu-pop" role="group" aria-label="Diff view">127 <div class="menu-pop" role="group" aria-label="Diff view">
128-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>128+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
129 </a>129 </a>
130-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>130+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
131 </a>131 </a>
132 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">132 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
133 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>133 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-modify-split.html +3 -3
@@ -129,12 +129,12 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
129129 </span>
130130 <details class="menu vmenu">
131131 <summary class="btn" aria-label="Diff view">
132-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Split</span>
132+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Split</span>
133133 </summary>
134134 <div class="menu-pop" role="group" aria-label="Diff view">
135-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
135+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
136136 </a>
137-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" aria-current="true" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
137+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" aria-current="true" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
138138 </a>
139139 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
140140 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -129,12 +129,12 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
129 </span>129 </span>
130 <details class="menu vmenu">130 <details class="menu vmenu">
131 <summary class="btn" aria-label="Diff view">131 <summary class="btn" aria-label="Diff view">
132-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Split</span>132+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Split</span>
133 </summary>133 </summary>
134 <div class="menu-pop" role="group" aria-label="Diff view">134 <div class="menu-pop" role="group" aria-label="Diff view">
135-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>135+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
136 </a>136 </a>
137-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" aria-current="true" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>137+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" aria-current="true" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
138 </a>138 </a>
139 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">139 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
140 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>140 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-modify.html +3 -3
@@ -129,12 +129,12 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
129129 </span>
130130 <details class="menu vmenu">
131131 <summary class="btn" aria-label="Diff view">
132-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
132+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
133133 </summary>
134134 <div class="menu-pop" role="group" aria-label="Diff view">
135-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
135+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
136136 </a>
137-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
137+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
138138 </a>
139139 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
140140 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -129,12 +129,12 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
129 </span>129 </span>
130 <details class="menu vmenu">130 <details class="menu vmenu">
131 <summary class="btn" aria-label="Diff view">131 <summary class="btn" aria-label="Diff view">
132-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>132+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
133 </summary>133 </summary>
134 <div class="menu-pop" role="group" aria-label="Diff view">134 <div class="menu-pop" role="group" aria-label="Diff view">
135-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>135+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
136 </a>136 </a>
137-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>137+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
138 </a>138 </a>
139 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">139 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
140 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>140 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-rename.html +3 -3
@@ -123,12 +123,12 @@
123123 </span>
124124 <details class="menu vmenu">
125125 <summary class="btn" aria-label="Diff view">
126-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
126+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
127127 </summary>
128128 <div class="menu-pop" role="group" aria-label="Diff view">
129-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
129+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
130130 </a>
131-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
131+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
132132 </a>
133133 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
134134 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -123,12 +123,12 @@
123 </span>123 </span>
124 <details class="menu vmenu">124 <details class="menu vmenu">
125 <summary class="btn" aria-label="Diff view">125 <summary class="btn" aria-label="Diff view">
126-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>126+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
127 </summary>127 </summary>
128 <div class="menu-pop" role="group" aria-label="Diff view">128 <div class="menu-pop" role="group" aria-label="Diff view">
129-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>129+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
130 </a>130 </a>
131-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>131+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
132 </a>132 </a>
133 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">133 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
134 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>134 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-root.html +3 -3
@@ -121,12 +121,12 @@
121121 </span>
122122 <details class="menu vmenu">
123123 <summary class="btn" aria-label="Diff view">
124-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
124+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
125125 </summary>
126126 <div class="menu-pop" role="group" aria-label="Diff view">
127-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
127+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
128128 </a>
129-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
129+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
130130 </a>
131131 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
132132 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -121,12 +121,12 @@
121 </span>121 </span>
122 <details class="menu vmenu">122 <details class="menu vmenu">
123 <summary class="btn" aria-label="Diff view">123 <summary class="btn" aria-label="Diff view">
124-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>124+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
125 </summary>125 </summary>
126 <div class="menu-pop" role="group" aria-label="Diff view">126 <div class="menu-pop" role="group" aria-label="Diff view">
127-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>127+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
128 </a>128 </a>
129-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>129+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
130 </a>130 </a>
131 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">131 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
132 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>132 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commit-trojan.html +3 -3
@@ -124,12 +124,12 @@
124124 </span>
125125 <details class="menu vmenu">
126126 <summary class="btn" aria-label="Diff view">
127-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
127+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
128128 </summary>
129129 <div class="menu-pop" role="group" aria-label="Diff view">
130-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
130+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
131131 </a>
132-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
132+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
133133 </a>
134134 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
135135 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
@@ -124,12 +124,12 @@
124 </span>124 </span>
125 <details class="menu vmenu">125 <details class="menu vmenu">
126 <summary class="btn" aria-label="Diff view">126 <summary class="btn" aria-label="Diff view">
127-<span hx-live="this.textContent = q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>127+<span hx-live="this.textContent = q('#diffs').attr['data-mobile'] !== 'true' && q('#diffs').attr['data-diff'] === 'split' ? 'Split' : 'Unified'">Unified</span>
128 </summary>128 </summary>
129 <div class="menu-pop" role="group" aria-label="Diff view">129 <div class="menu-pop" role="group" aria-label="Diff view">
130-<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>130+<a href="/diffs/commit/SHA40?view=unified" class="btn vbtn" data-view="unified" aria-keyshortcuts="u" aria-current="true" data-fire="unified" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Unified<kbd aria-hidden="true">u</kbd>
131 </a>131 </a>
132-<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" data-fire="split" :aria-current="q('#diffs').attr['data-diff'] === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>132+<a href="/diffs/commit/SHA40?view=split" class="btn vbtn" data-view="split" aria-keyshortcuts="d" :aria-disabled="q('#diffs').attr['data-mobile'] === 'true' ? 'true' : false" :title="q('#diffs').attr['data-mobile'] === 'true' ? 'Split view requires a wider screen' : 'Split view'" data-fire="split" :aria-current="(q('#diffs').attr['data-mobile'] === 'true' ? 'unified' : q('#diffs').attr['data-diff']) === this.dataset.view ? 'true' : false">Split<kbd aria-hidden="true">d</kbd>
133 </a>133 </a>
134 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">134 <button type="button" aria-keyshortcuts="x" data-js data-fire="collapse" class="btn">
135 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>135 <span hx-live="this.textContent = q('[data-file][open]').count || !q('[data-file]').count ? 'Collapse all' : 'Expand all'">Collapse all</span>
M internal/app/testdata/web/commits-root.html +1 -0
@@ -973,6 +973,7 @@
973973 htmx:sse:error -> ermiteLive.error(event.detail.status);
974974 htmx:sse:before:connection -> ermiteLive.gate(event);
975975 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
976+ mr-head -> ermiteMR.headChanged();
976977 gap -> ermiteLive.reload()">
977978 </span>
978979 </div>
@@ -973,6 +973,7 @@
973 htmx:sse:error -> ermiteLive.error(event.detail.status);973 htmx:sse:error -> ermiteLive.error(event.detail.status);
974 htmx:sse:before:connection -> ermiteLive.gate(event);974 htmx:sse:before:connection -> ermiteLive.gate(event);
975 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);975 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
976+ mr-head -> ermiteMR.headChanged();
976 gap -> ermiteLive.reload()">977 gap -> ermiteLive.reload()">
977 </span>978 </span>
978 </div>979 </div>
M internal/app/testdata/web/commits-small.html +1 -0
@@ -318,6 +318,7 @@
318318 htmx:sse:error -> ermiteLive.error(event.detail.status);
319319 htmx:sse:before:connection -> ermiteLive.gate(event);
320320 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
321+ mr-head -> ermiteMR.headChanged();
321322 gap -> ermiteLive.reload()">
322323 </span>
323324 </div>
@@ -318,6 +318,7 @@
318 htmx:sse:error -> ermiteLive.error(event.detail.status);318 htmx:sse:error -> ermiteLive.error(event.detail.status);
319 htmx:sse:before:connection -> ermiteLive.gate(event);319 htmx:sse:before:connection -> ermiteLive.gate(event);
320 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);320 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
321+ mr-head -> ermiteMR.headChanged();
321 gap -> ermiteLive.reload()">322 gap -> ermiteLive.reload()">
322 </span>323 </span>
323 </div>324 </div>
M internal/app/testdata/web/home.html +1 -0
@@ -194,6 +194,7 @@
194194 htmx:sse:error -> ermiteLive.error(event.detail.status);
195195 htmx:sse:before:connection -> ermiteLive.gate(event);
196196 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
197+ mr-head -> ermiteMR.headChanged();
197198 gap -> ermiteLive.reload()">
198199 </span>
199200 </div>
@@ -194,6 +194,7 @@
194 htmx:sse:error -> ermiteLive.error(event.detail.status);194 htmx:sse:error -> ermiteLive.error(event.detail.status);
195 htmx:sse:before:connection -> ermiteLive.gate(event);195 htmx:sse:before:connection -> ermiteLive.gate(event);
196 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);196 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
197+ mr-head -> ermiteMR.headChanged();
197 gap -> ermiteLive.reload()">198 gap -> ermiteLive.reload()">
198 </span>199 </span>
199 </div>200 </div>
M internal/app/testdata/web/refs.html +1 -0
@@ -200,6 +200,7 @@
200200 htmx:sse:error -> ermiteLive.error(event.detail.status);
201201 htmx:sse:before:connection -> ermiteLive.gate(event);
202202 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
203+ mr-head -> ermiteMR.headChanged();
203204 gap -> ermiteLive.reload()">
204205 </span>
205206 </div>
@@ -200,6 +200,7 @@
200 htmx:sse:error -> ermiteLive.error(event.detail.status);200 htmx:sse:error -> ermiteLive.error(event.detail.status);
201 htmx:sse:before:connection -> ermiteLive.gate(event);201 htmx:sse:before:connection -> ermiteLive.gate(event);
202 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);202 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
203+ mr-head -> ermiteMR.headChanged();
203 gap -> ermiteLive.reload()">204 gap -> ermiteLive.reload()">
204 </span>205 </span>
205 </div>206 </div>
M internal/app/testdata/web/summary.html +1 -0
@@ -226,6 +226,7 @@
226226 htmx:sse:error -> ermiteLive.error(event.detail.status);
227227 htmx:sse:before:connection -> ermiteLive.gate(event);
228228 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
229+ mr-head -> ermiteMR.headChanged();
229230 gap -> ermiteLive.reload()">
230231 </span>
231232 </div>
@@ -226,6 +226,7 @@
226 htmx:sse:error -> ermiteLive.error(event.detail.status);226 htmx:sse:error -> ermiteLive.error(event.detail.status);
227 htmx:sse:before:connection -> ermiteLive.gate(event);227 htmx:sse:before:connection -> ermiteLive.gate(event);
228 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);228 home-list, repo-last-push, refs-list, commits-head, mr-notice, mr-mergeability, ci-run, ci-job, inbox -> ermiteLive.swap(event.detail.data);
229+ mr-head -> ermiteMR.headChanged();
229 gap -> ermiteLive.reload()">230 gap -> ermiteLive.reload()">
230 </span>231 </span>
231 </div>232 </div>
M internal/app/web_events_test.go +12 -5
@@ -245,12 +245,19 @@ func TestWebEventsResumeAndGap(t *testing.T) {
245245 }
246246 s.cancel()
247247
248- r := h.sse("topic=home", ids[0])
249- for _, want := range ids[1:] {
250- ev := r.next(t, watchWait)
251- assert.Equal(t, want, ev.id)
252- assert.Equal(t, "home-list", ev.name)
248+ // A replacement stream resumes via the URL; reconnect headers still win.
249+ for _, r := range []*sseStream{
250+ h.sse("topic=home&after="+ids[0], ""),
251+ h.sse("topic=home&after=invalid", ids[0]),
252+ } {
253+ for _, want := range ids[1:] {
254+ ev := r.next(t, watchWait)
255+ assert.Equal(t, want, ev.id)
256+ assert.Equal(t, "home-list", ev.name)
257+ }
258+ r.cancel()
253259 }
260+ r := h.sse("topic=home", ids[2])
254261 h.writeFile(wt, "live.txt", []byte("x\n"))
255262 h.mustGit(wt, "push", "-q", h.urlAuth(adminToken, "/pub.git"), "main")
256263 live := r.next(t, watchWait)
@@ -245,12 +245,19 @@ func TestWebEventsResumeAndGap(t *testing.T) {
245 }245 }
246 s.cancel()246 s.cancel()
247 247
248- r := h.sse("topic=home", ids[0])248+ // A replacement stream resumes via the URL; reconnect headers still win.
249- for _, want := range ids[1:] {249+ for _, r := range []*sseStream{
250- ev := r.next(t, watchWait)250+ h.sse("topic=home&after="+ids[0], ""),
251- assert.Equal(t, want, ev.id)251+ h.sse("topic=home&after=invalid", ids[0]),
252- assert.Equal(t, "home-list", ev.name)252+ } {
253+ for _, want := range ids[1:] {
254+ ev := r.next(t, watchWait)
255+ assert.Equal(t, want, ev.id)
256+ assert.Equal(t, "home-list", ev.name)
257+ }
258+ r.cancel()
253 }259 }
260+ r := h.sse("topic=home", ids[2])
254 h.writeFile(wt, "live.txt", []byte("x\n"))261 h.writeFile(wt, "live.txt", []byte("x\n"))
255 h.mustGit(wt, "push", "-q", h.urlAuth(adminToken, "/pub.git"), "main")262 h.mustGit(wt, "push", "-q", h.urlAuth(adminToken, "/pub.git"), "main")
256 live := r.next(t, watchWait)263 live := r.next(t, watchWait)
M internal/app/web_mr_test.go +4 -3
@@ -335,7 +335,7 @@ func TestWebMergeRequestReviewAndFastForward(t *testing.T) {
335335 all := b.get(base, as(token))
336336 assert.Contains(t, all.body, `href="/proj/merge-requests/1/since"`)
337337 assert.Contains(t, all.body, "since your last read")
338- assert.Contains(t, all.body, "<li data-new>", "the commit pushed since is marked")
338+ assert.Contains(t, all.body, "<tr data-new>", "the commit pushed since is marked")
339339 assert.Contains(t, all.body, "data-read>Mark read")
340340 assert.Contains(t, all.body, "outdated", "the notes were written on the earlier head")
341341 since := b.get(base+"/since", as(token))
@@ -595,8 +595,9 @@ func TestWebMergeRequestEvents(t *testing.T) {
595595
596596 r.writeFile(r.wt, "more.txt", []byte("more\n"))
597597 r.mustGit(r.wt, "push", "-q", r.url, "feat/search")
598- ev = nextNamed(t, one, "mr-notice")
599- assert.Contains(t, ev.data, "the branch changed")
598+ ev = nextNamed(t, one, "mr-head")
599+ assert.Equal(t, `<div id="mr-head"></div>`, ev.data)
600+ assert.NotContains(t, ev.data, "the branch changed")
600601
601602 r.cli(t, adminToken, "mr", "close", "proj", "1").ok(t)
602603 assert.Contains(t, nextNamed(t, one, "mr-notice").data, "was closed")
@@ -335,7 +335,7 @@ func TestWebMergeRequestReviewAndFastForward(t *testing.T) {
335 all := b.get(base, as(token))335 all := b.get(base, as(token))
336 assert.Contains(t, all.body, `href="/proj/merge-requests/1/since"`)336 assert.Contains(t, all.body, `href="/proj/merge-requests/1/since"`)
337 assert.Contains(t, all.body, "since your last read")337 assert.Contains(t, all.body, "since your last read")
338- assert.Contains(t, all.body, "<li data-new>", "the commit pushed since is marked")338+ assert.Contains(t, all.body, "<tr data-new>", "the commit pushed since is marked")
339 assert.Contains(t, all.body, "data-read>Mark read")339 assert.Contains(t, all.body, "data-read>Mark read")
340 assert.Contains(t, all.body, "outdated", "the notes were written on the earlier head")340 assert.Contains(t, all.body, "outdated", "the notes were written on the earlier head")
341 since := b.get(base+"/since", as(token))341 since := b.get(base+"/since", as(token))
@@ -595,8 +595,9 @@ func TestWebMergeRequestEvents(t *testing.T) {
595 595
596 r.writeFile(r.wt, "more.txt", []byte("more\n"))596 r.writeFile(r.wt, "more.txt", []byte("more\n"))
597 r.mustGit(r.wt, "push", "-q", r.url, "feat/search")597 r.mustGit(r.wt, "push", "-q", r.url, "feat/search")
598- ev = nextNamed(t, one, "mr-notice")598+ ev = nextNamed(t, one, "mr-head")
599- assert.Contains(t, ev.data, "the branch changed")599+ assert.Equal(t, `<div id="mr-head"></div>`, ev.data)
600+ assert.NotContains(t, ev.data, "the branch changed")
600 601
601 r.cli(t, adminToken, "mr", "close", "proj", "1").ok(t)602 r.cli(t, adminToken, "mr", "close", "proj", "1").ok(t)
602 assert.Contains(t, nextNamed(t, one, "mr-notice").data, "was closed")603 assert.Contains(t, nextNamed(t, one, "mr-notice").data, "was closed")

Comments0 open / 0

no comments yet.

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
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close