loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Port] gitea#30014: Various code view improvements

1. Restore missing styles for message close icon
2. Move `code-line-button` so that it does not go off-screen on small
viewports
3. Make `code-line-button` look and behave like other buttons
4. Make `code-line-button` work in blame
5. Make the active selection span the whole line, not just the code part
6. Tweak colors, make dark theme code bg darker, make line numbers same
color in diff and file view.
7. Move code background to parent, fixing border radius and other
problems
8. Enable code wrap in blame
9. Improve blame responsiveness
10. Remove `--color-code-sidebar-bg` in blame, now it uses same
background as code
11. Rename `--color-active-line` to `--color-highlight-bg`
12. Add `--color-highlight-bg`
13. Fix button group borders on hover and border-right on last button.

<img width="1343" alt="Screenshot 2024-03-23 at 22 34 13"
src="https://github.com/go-gitea/gitea/assets/115237/fcbb919f-5dc3-43f0-97f6-870d6f412554">
<img width="1334" alt="Screenshot 2024-03-23 at 22 34 26"
src="https://github.com/go-gitea/gitea/assets/115237/ca44c3b7-4328-4645-ba49-b0dc6a5ac06d">

<img width="1338" alt="Screenshot 2024-03-23 at 22 34 57"
src="https://github.com/go-gitea/gitea/assets/115237/00eb0b5a-1ec7-4669-a94a-4602b9d1c1ac">
<img width="1337" alt="Screenshot 2024-03-23 at 22 34 42"
src="https://github.com/go-gitea/gitea/assets/115237/752edc4a-064f-413c-9dff-c086187fcd85">

Fixes: https://github.com/go-gitea/gitea/issues/18074

---

Conflict resolution: Trivial.
Ref: https://codeberg.org/forgejo/forgejo/issues/2776
(cherry picked from commit db01bf6cc88a8a7b5132b9306b3af1649566b10f)

authored by

silverwind and committed by
Gusted
a579a0f3 1ee494a0

+145 -103
+1 -1
routers/web/repo/blame.go
··· 258 258 259 259 var avatar string 260 260 if commit.User != nil { 261 - avatar = string(avatarUtils.Avatar(commit.User, 18, "gt-mr-3")) 261 + avatar = string(avatarUtils.Avatar(commit.User, 18)) 262 262 } else { 263 263 avatar = string(avatarUtils.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "gt-mr-3")) 264 264 }
+7
templates/devtest/gitea-ui.tmpl
··· 2 2 <link rel="stylesheet" href="{{AssetUrlPrefix}}/css/devtest.css?v={{AssetVersion}}"> 3 3 <div class="page-content devtest ui container"> 4 4 <div> 5 + <h1>Link</h1> 6 + <div> 7 + <a href="#">normal</a> 8 + <a class="muted" href="#">muted</a> 9 + <a class="suppressed" href="#">suppressed</a> 10 + <a class="silenced" href="#">silenced</a> 11 + </div> 5 12 <h1>Button</h1> 6 13 <div> 7 14 Style:
+9 -3
templates/repo/blame.tmpl
··· 50 50 {{$row.Avatar}} 51 51 </div> 52 52 <div class="blame-message"> 53 - <a href="{{$row.CommitURL}}" title="{{$row.CommitMessage}}"> 53 + <a class="suppressed tw-text-text" href="{{$row.CommitURL}}" title="{{$row.CommitMessage}}"> 54 54 {{$row.CommitMessage}} 55 55 </a> 56 56 </div> 57 - <div class="blame-time"> 57 + <div class="blame-time not-mobile"> 58 58 {{$row.CommitSince}} 59 59 </div> 60 60 </div> ··· 62 62 </td> 63 63 <td class="lines-blame-btn"> 64 64 {{if $row.PreviousSha}} 65 - <a href="{{$row.PreviousShaURL}}" data-tooltip-content='{{ctx.Locale.Tr "repo.blame_prior"}}'> 65 + <a role="button" class="muted" href="{{$row.PreviousShaURL}}" data-tooltip-content='{{ctx.Locale.Tr "repo.blame_prior"}}'> 66 66 {{svg "octicon-versions"}} 67 67 </a> 68 68 {{end}} ··· 84 84 {{end}} 85 85 </tbody> 86 86 </table> 87 + <div class="code-line-menu tippy-target"> 88 + {{if $.Permission.CanRead $.UnitTypeIssues}} 89 + <a class="item ref-in-new-issue" role="menuitem" data-url-issue-new="{{.RepoLink}}/issues/new" data-url-param-body-link="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}" rel="nofollow noindex">{{ctx.Locale.Tr "repo.issues.context.reference_issue"}}</a> 90 + {{end}} 91 + <a class="item copy-line-permalink" role="menuitem" data-url="{{.Repository.Link}}/src/commit/{{PathEscape .CommitID}}/{{PathEscapeSegments .TreePath}}{{if $.HasSourceRenderedToggle}}?display=source{{end}}">{{ctx.Locale.Tr "repo.file_copy_permalink"}}</a> 92 + </div> 87 93 {{end}} 88 94 </div> 89 95 </div>
+47 -33
web_src/css/base.css
··· 226 226 a { 227 227 color: var(--color-primary); 228 228 cursor: pointer; 229 - text-decoration: none; 229 + text-decoration-line: none; 230 230 text-decoration-skip-ink: all; 231 + } 232 + 233 + a:hover { 234 + text-decoration-line: underline; 231 235 } 232 236 233 237 /* a = always colored, underlined on hover */ ··· 256 260 } 257 261 258 262 a.silenced:hover { 259 - text-decoration: none; 263 + text-decoration-line: none; 260 264 } 261 265 262 266 a.label, ··· 264 268 .ui .menu a, 265 269 .ui.cards a.card, 266 270 .issue-keyword a { 267 - text-decoration: none !important; 271 + text-decoration-line: none !important; 268 272 } 269 273 270 274 .ui.search > .results { ··· 1433 1437 } 1434 1438 1435 1439 .lines-blame-btn { 1436 - padding-left: 10px; 1437 - padding-right: 10px; 1438 - text-align: right !important; 1439 - background-color: var(--color-code-sidebar-bg); 1440 - width: 2%; 1440 + padding: 0 0 0 5px; 1441 + display: flex; 1442 + justify-content: center; 1441 1443 } 1442 1444 1443 1445 .lines-num { 1444 - padding-left: 10px; 1445 - padding-right: 10px; 1446 + padding: 0 8px; 1446 1447 text-align: right !important; 1447 - color: var(--color-text-light-1); 1448 + color: var(--color-text-light-2); 1448 1449 width: 1%; 1449 1450 font-family: var(--fonts-monospace); 1450 1451 } ··· 1498 1499 } 1499 1500 1500 1501 .lines-code { 1501 - background-color: var(--color-code-bg); 1502 1502 padding-left: 5px; 1503 1503 } 1504 1504 1505 - .lines-code.active, 1506 - .lines-code .active { 1507 - background: var(--color-active-line) !important; 1505 + .file-view tr.active { 1506 + color: inherit !important; 1507 + background: inherit !important; 1508 1508 } 1509 1509 1510 - .blame .lines-num { 1511 - padding: 0 !important; 1512 - background-color: var(--color-code-sidebar-bg); 1510 + .file-view tr.active .lines-num, 1511 + .file-view tr.active .lines-code { 1512 + background: var(--color-highlight-bg) !important; 1513 + } 1514 + 1515 + .file-view tr.active:last-of-type .lines-code { 1516 + border-bottom-right-radius: var(--border-radius); 1513 1517 } 1514 1518 1515 - .blame .lines-code { 1516 - padding: 0 !important; 1519 + .file-view tr.active .lines-num { 1520 + position: relative; 1521 + } 1522 + 1523 + .file-view tr.active .lines-num::before { 1524 + content: ""; 1525 + position: absolute; 1526 + left: 0; 1527 + width: 2px; 1528 + height: 100%; 1529 + background: var(--color-highlight-fg); 1517 1530 } 1518 1531 1519 1532 .code-inner { ··· 1524 1537 } 1525 1538 1526 1539 .blame .code-inner { 1527 - white-space: pre; 1528 - word-break: normal; 1529 - word-wrap: normal; /* not using overflow-wrap because safari does not treat is an an alias */ 1540 + white-space: pre-wrap; 1541 + overflow-wrap: anywhere; 1530 1542 } 1531 1543 1532 1544 .lines-commit { 1533 1545 vertical-align: top; 1534 - color: var(--color-text-light-2); 1546 + color: var(--color-text-light-1); 1535 1547 padding: 0 !important; 1536 - background: var(--color-code-sidebar-bg); 1537 1548 width: 1%; 1538 1549 } 1539 1550 1540 1551 .lines-commit .blame-info { 1541 - width: 350px; 1542 - max-width: 350px; 1552 + width: min(26vw, 300px); 1543 1553 display: block; 1544 - padding: 0 0 0 10px; 1554 + padding: 0 0 0 6px; 1545 1555 line-height: 20px; 1546 1556 box-sizing: content-box; 1547 1557 } ··· 1563 1573 flex-shrink: 0; 1564 1574 } 1565 1575 1566 - .lines-commit .ui.avatar { 1567 - height: 18px; 1568 - width: 18px; 1569 - display: block; 1570 - margin-top: 1px; 1576 + .blame-avatar { 1577 + display: flex; 1578 + align-items: center; 1579 + margin-right: 4px; 1571 1580 } 1572 1581 1573 1582 .top-line-blame { ··· 1581 1590 .lines-code .bottom-line, 1582 1591 .lines-commit .bottom-line { 1583 1592 border-bottom: 1px solid var(--color-secondary); 1593 + } 1594 + 1595 + .code-view { 1596 + background: var(--color-code-bg); 1597 + border-radius: var(--border-radius); 1584 1598 } 1585 1599 1586 1600 .code-view table {
-4
web_src/css/chroma/base.css
··· 1 - .chroma { 2 - background-color: var(--color-code-bg); 3 - } 4 - 5 1 /* LineTableTD */ 6 2 .chroma .lntd { 7 3 vertical-align: top;
+10 -2
web_src/css/modules/button.css
··· 11 11 .ui.button:focus { 12 12 background: var(--color-hover); 13 13 color: var(--color-text); 14 + border-color: var(--color-secondary-dark-2); 14 15 } 15 16 16 17 .page-content .ui.button { ··· 63 64 border-right: none; 64 65 } 65 66 66 - .ui.buttons .button:first-child { 67 + .ui.buttons .button:hover + .button { 68 + border-left: 1px solid var(--color-secondary-dark-2); 69 + } 70 + 71 + .ui.buttons .button:first-child, 72 + .ui.buttons .button.gt-hidden:first-child + .button { 67 73 border-left: 1px solid var(--color-light-border); 68 74 } 69 75 70 - .ui.buttons .button:last-child { 76 + .ui.buttons .button:last-child, 77 + .ui.buttons .button:nth-last-child(2):has(+ .button.gt-hidden) { 71 78 border-right: 1px solid var(--color-light-border); 72 79 } 73 80 ··· 107 114 .ui.basic.button:focus { 108 115 color: var(--color-text); 109 116 background: var(--color-hover); 117 + border-color: var(--color-secondary-dark-2); 110 118 } 111 119 112 120 .ui.basic.buttons .button:active,
+12
web_src/css/modules/message.css
··· 100 100 color: var(--color-warning-text); 101 101 border-color: var(--color-warning-border); 102 102 } 103 + 104 + .ui.message > .close.icon { 105 + cursor: pointer; 106 + position: absolute; 107 + top: 9px; 108 + right: 9px; 109 + opacity: .7; 110 + } 111 + 112 + .ui.message > .close.icon:hover { 113 + opacity: 1; 114 + }
-1
web_src/css/repo.css
··· 1601 1601 1602 1602 .repository .diff-file-box .file-body.file-code .lines-num { 1603 1603 text-align: right; 1604 - color: var(--color-text-light); 1605 1604 width: 1%; 1606 1605 min-width: 50px; 1607 1606 }
+3 -5
web_src/css/repo/linebutton.css
··· 3 3 } 4 4 5 5 .code-line-button { 6 - background-color: var(--color-menu); 7 - color: var(--color-text-light); 8 6 border: 1px solid var(--color-secondary); 9 7 border-radius: var(--border-radius); 10 - padding: 1px 10px; 8 + padding: 1px 4px !important; 11 9 position: absolute; 12 10 font-family: var(--fonts-regular); 13 11 left: 0; 14 - transform: translateX(-50%); 12 + transform: translateX(calc(-50% + 6px)); 15 13 cursor: pointer; 16 14 } 17 15 18 16 .code-line-button:hover { 19 - color: var(--color-primary); 17 + background: var(--color-secondary) !important; 20 18 }
+6 -6
web_src/css/themes/theme-gitea-dark.css
··· 183 183 --color-body: #1c1f25; 184 184 --color-box-header: #1a1d1f; 185 185 --color-box-body: #14171a; 186 - --color-box-body-highlight: #121517; 186 + --color-box-body-highlight: #1c2227; 187 187 --color-text-dark: #f8f8f9; 188 188 --color-text: #d1d5d8; 189 189 --color-text-light: #bdc3c7; ··· 207 207 --color-markup-table-row: #e8e8ff06; 208 208 --color-markup-code-block: #e8e8ff16; 209 209 --color-button: #151a1e; 210 - --color-code-bg: #191d20; 211 - --color-code-sidebar-bg: #1b1f22; 210 + --color-code-bg: #14171a; 212 211 --color-shadow: #00001758; 213 - --color-secondary-bg: #2f3135; 214 - --color-expand-button: #414348; 212 + --color-secondary-bg: #2f3138; 213 + --color-expand-button: #2b353e; 215 214 --color-placeholder-text: var(--color-text-light-3); 216 215 --color-editor-line-highlight: var(--color-primary-light-5); 217 216 --color-project-board-bg: var(--color-secondary-light-2); ··· 233 232 --color-label-active-bg: #73828eff; 234 233 --color-accent: var(--color-primary-light-1); 235 234 --color-small-accent: var(--color-primary-light-5); 236 - --color-active-line: #534d1b; 235 + --color-highlight-fg: #87651e; 236 + --color-highlight-bg: #352c1c; 237 237 --color-overlay-backdrop: #080808c0; 238 238 accent-color: var(--color-accent); 239 239 color-scheme: dark;
+4 -4
web_src/css/themes/theme-gitea-light.css
··· 183 183 --color-body: #ffffff; 184 184 --color-box-header: #f1f3f5; 185 185 --color-box-body: #ffffff; 186 - --color-box-body-highlight: #f4faff; 186 + --color-box-body-highlight: #ecf5fd; 187 187 --color-text-dark: #01050a; 188 188 --color-text: #181c21; 189 189 --color-text-light: #30363b; ··· 208 208 --color-markup-code-block: #00001710; 209 209 --color-button: #f8f9fb; 210 210 --color-code-bg: #fafdff; 211 - --color-code-sidebar-bg: #f2f5f8; 212 211 --color-shadow: #00001726; 213 212 --color-secondary-bg: #f2f5f8; 214 - --color-expand-button: #d8efff; 213 + --color-expand-button: #cfe8fa; 215 214 --color-placeholder-text: var(--color-text-light-3); 216 215 --color-editor-line-highlight: var(--color-primary-light-6); 217 216 --color-project-board-bg: var(--color-secondary-light-4); ··· 233 232 --color-label-active-bg: #949da6ff; 234 233 --color-accent: var(--color-primary-light-1); 235 234 --color-small-accent: var(--color-primary-light-6); 236 - --color-active-line: #fffbdd; 235 + --color-highlight-fg: #eed200; 236 + --color-highlight-bg: #fffbdd; 237 237 --color-overlay-backdrop: #080808c0; 238 238 accent-color: var(--color-accent); 239 239 color-scheme: light;
+46 -44
web_src/js/features/repo-code.js
··· 16 16 } 17 17 } 18 18 19 - function selectRange($list, $select, $from) { 20 - $list.removeClass('active'); 19 + function isBlame() { 20 + return Boolean(document.querySelector('div.blame')); 21 + } 22 + 23 + function getLineEls() { 24 + return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`); 25 + } 26 + 27 + function selectRange($linesEls, $selectionEndEl, $selectionStartEls) { 28 + $linesEls.closest('tr').removeClass('active'); 21 29 22 30 // add hashchange to permalink 23 31 const $refInNewIssue = $('a.ref-in-new-issue'); ··· 25 33 const $viewGitBlame = $('a.view_git_blame'); 26 34 27 35 const updateIssueHref = function (anchor) { 28 - if ($refInNewIssue.length === 0) { 36 + if (!$refInNewIssue.length) { 29 37 return; 30 38 } 31 39 const urlIssueNew = $refInNewIssue.attr('data-url-issue-new'); ··· 35 43 }; 36 44 37 45 const updateViewGitBlameFragment = function (anchor) { 38 - if ($viewGitBlame.length === 0) { 39 - return; 40 - } 46 + if (!$viewGitBlame.length) return; 41 47 let href = $viewGitBlame.attr('href'); 42 48 href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`; 43 49 if (anchor.length !== 0) { ··· 47 53 }; 48 54 49 55 const updateCopyPermalinkUrl = function(anchor) { 50 - if ($copyPermalink.length === 0) { 51 - return; 52 - } 56 + if (!$copyPermalink.length) return; 53 57 let link = $copyPermalink.attr('data-url'); 54 58 link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`; 55 59 $copyPermalink.attr('data-url', link); 56 60 }; 57 61 58 - if ($from) { 59 - let a = parseInt($select.attr('rel').slice(1)); 60 - let b = parseInt($from.attr('rel').slice(1)); 62 + if ($selectionStartEls) { 63 + let a = parseInt($selectionEndEl.attr('rel').slice(1)); 64 + let b = parseInt($selectionStartEls.attr('rel').slice(1)); 61 65 let c; 62 66 if (a !== b) { 63 67 if (a > b) { ··· 69 73 for (let i = a; i <= b; i++) { 70 74 classes.push(`[rel=L${i}]`); 71 75 } 72 - $list.filter(classes.join(',')).addClass('active'); 76 + $linesEls.filter(classes.join(',')).each(function () { 77 + $(this).closest('tr').addClass('active'); 78 + }); 73 79 changeHash(`#L${a}-L${b}`); 74 80 75 81 updateIssueHref(`L${a}-L${b}`); ··· 78 84 return; 79 85 } 80 86 } 81 - $select.addClass('active'); 82 - changeHash(`#${$select.attr('rel')}`); 87 + $selectionEndEl.closest('tr').addClass('active'); 88 + changeHash(`#${$selectionEndEl.attr('rel')}`); 83 89 84 - updateIssueHref($select.attr('rel')); 85 - updateViewGitBlameFragment($select.attr('rel')); 86 - updateCopyPermalinkUrl($select.attr('rel')); 90 + updateIssueHref($selectionEndEl.attr('rel')); 91 + updateViewGitBlameFragment($selectionEndEl.attr('rel')); 92 + updateCopyPermalinkUrl($selectionEndEl.attr('rel')); 87 93 } 88 94 89 95 function showLineButton() { ··· 96 102 } 97 103 98 104 // find active row and add button 99 - const tr = document.querySelector('.code-view td.lines-code.active').closest('tr'); 100 - const td = tr.querySelector('td'); 105 + const tr = document.querySelector('.code-view tr.active'); 106 + const td = tr.querySelector('td.lines-num'); 101 107 const btn = document.createElement('button'); 102 - btn.classList.add('code-line-button'); 108 + btn.classList.add('code-line-button', 'ui', 'basic', 'button'); 103 109 btn.innerHTML = svg('octicon-kebab-horizontal'); 104 110 td.prepend(btn); 105 111 ··· 123 129 export function initRepoCodeView() { 124 130 if ($('.code-view .lines-num').length > 0) { 125 131 $(document).on('click', '.lines-num span', function (e) { 126 - const $select = $(this); 127 - let $list; 128 - if ($('div.blame').length) { 129 - $list = $('.code-view td.lines-code.blame-code'); 130 - } else { 131 - $list = $('.code-view td.lines-code'); 132 + const linesEls = getLineEls(); 133 + const selectedEls = Array.from(linesEls).filter((el) => { 134 + return el.matches(`[rel=${this.getAttribute('id')}]`); 135 + }); 136 + 137 + let from; 138 + if (e.shiftKey) { 139 + from = Array.from(linesEls).filter((el) => { 140 + return el.closest('tr').classList.contains('active'); 141 + }); 132 142 } 133 - selectRange($list, $list.filter(`[rel=${$select.attr('id')}]`), (e.shiftKey ? $list.filter('.active').eq(0) : null)); 143 + selectRange($(linesEls), $(selectedEls), from ? $(from) : null); 134 144 135 145 if (window.getSelection) { 136 146 window.getSelection().removeAllRanges(); ··· 138 148 document.selection.empty(); 139 149 } 140 150 141 - // show code view menu marker (don't show in blame page) 142 - if ($('div.blame').length === 0) { 143 - showLineButton(); 144 - } 151 + showLineButton(); 145 152 }); 146 153 147 154 $(window).on('hashchange', () => { 148 155 let m = window.location.hash.match(rangeAnchorRegex); 149 - let $list; 150 - if ($('div.blame').length) { 151 - $list = $('.code-view td.lines-code.blame-code'); 152 - } else { 153 - $list = $('.code-view td.lines-code'); 154 - } 156 + const $linesEls = $(getLineEls()); 155 157 let $first; 156 158 if (m) { 157 - $first = $list.filter(`[rel=${m[1]}]`); 159 + $first = $linesEls.filter(`[rel=${m[1]}]`); 158 160 if ($first.length) { 159 - selectRange($list, $first, $list.filter(`[rel=${m[2]}]`)); 161 + selectRange($linesEls, $first, $linesEls.filter(`[rel=${m[2]}]`)); 160 162 161 163 // show code view menu marker (don't show in blame page) 162 - if ($('div.blame').length === 0) { 164 + if (!isBlame()) { 163 165 showLineButton(); 164 166 } 165 167 ··· 169 171 } 170 172 m = window.location.hash.match(singleAnchorRegex); 171 173 if (m) { 172 - $first = $list.filter(`[rel=L${m[2]}]`); 174 + $first = $linesEls.filter(`[rel=L${m[2]}]`); 173 175 if ($first.length) { 174 - selectRange($list, $first); 176 + selectRange($linesEls, $first); 175 177 176 178 // show code view menu marker (don't show in blame page) 177 - if ($('div.blame').length === 0) { 179 + if (!isBlame()) { 178 180 showLineButton(); 179 181 } 180 182