mirror of
https://codeberg.org/forgejo/forgejo
synced 2024-11-24 10:46:10 +01:00
a579a0f318
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)
195 lines
5.9 KiB
JavaScript
195 lines
5.9 KiB
JavaScript
import $ from 'jquery';
|
|
import {svg} from '../svg.js';
|
|
import {invertFileFolding} from './file-fold.js';
|
|
import {createTippy} from '../modules/tippy.js';
|
|
import {clippie} from 'clippie';
|
|
import {toAbsoluteUrl} from '../utils.js';
|
|
|
|
export const singleAnchorRegex = /^#(L|n)([1-9][0-9]*)$/;
|
|
export const rangeAnchorRegex = /^#(L[1-9][0-9]*)-(L[1-9][0-9]*)$/;
|
|
|
|
function changeHash(hash) {
|
|
if (window.history.pushState) {
|
|
window.history.pushState(null, null, hash);
|
|
} else {
|
|
window.location.hash = hash;
|
|
}
|
|
}
|
|
|
|
function isBlame() {
|
|
return Boolean(document.querySelector('div.blame'));
|
|
}
|
|
|
|
function getLineEls() {
|
|
return document.querySelectorAll(`.code-view td.lines-code${isBlame() ? '.blame-code' : ''}`);
|
|
}
|
|
|
|
function selectRange($linesEls, $selectionEndEl, $selectionStartEls) {
|
|
$linesEls.closest('tr').removeClass('active');
|
|
|
|
// add hashchange to permalink
|
|
const $refInNewIssue = $('a.ref-in-new-issue');
|
|
const $copyPermalink = $('a.copy-line-permalink');
|
|
const $viewGitBlame = $('a.view_git_blame');
|
|
|
|
const updateIssueHref = function (anchor) {
|
|
if (!$refInNewIssue.length) {
|
|
return;
|
|
}
|
|
const urlIssueNew = $refInNewIssue.attr('data-url-issue-new');
|
|
const urlParamBodyLink = $refInNewIssue.attr('data-url-param-body-link');
|
|
const issueContent = `${toAbsoluteUrl(urlParamBodyLink)}#${anchor}`; // the default content for issue body
|
|
$refInNewIssue.attr('href', `${urlIssueNew}?body=${encodeURIComponent(issueContent)}`);
|
|
};
|
|
|
|
const updateViewGitBlameFragment = function (anchor) {
|
|
if (!$viewGitBlame.length) return;
|
|
let href = $viewGitBlame.attr('href');
|
|
href = `${href.replace(/#L\d+$|#L\d+-L\d+$/, '')}`;
|
|
if (anchor.length !== 0) {
|
|
href = `${href}#${anchor}`;
|
|
}
|
|
$viewGitBlame.attr('href', href);
|
|
};
|
|
|
|
const updateCopyPermalinkUrl = function(anchor) {
|
|
if (!$copyPermalink.length) return;
|
|
let link = $copyPermalink.attr('data-url');
|
|
link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`;
|
|
$copyPermalink.attr('data-url', link);
|
|
};
|
|
|
|
if ($selectionStartEls) {
|
|
let a = parseInt($selectionEndEl.attr('rel').slice(1));
|
|
let b = parseInt($selectionStartEls.attr('rel').slice(1));
|
|
let c;
|
|
if (a !== b) {
|
|
if (a > b) {
|
|
c = a;
|
|
a = b;
|
|
b = c;
|
|
}
|
|
const classes = [];
|
|
for (let i = a; i <= b; i++) {
|
|
classes.push(`[rel=L${i}]`);
|
|
}
|
|
$linesEls.filter(classes.join(',')).each(function () {
|
|
$(this).closest('tr').addClass('active');
|
|
});
|
|
changeHash(`#L${a}-L${b}`);
|
|
|
|
updateIssueHref(`L${a}-L${b}`);
|
|
updateViewGitBlameFragment(`L${a}-L${b}`);
|
|
updateCopyPermalinkUrl(`L${a}-L${b}`);
|
|
return;
|
|
}
|
|
}
|
|
$selectionEndEl.closest('tr').addClass('active');
|
|
changeHash(`#${$selectionEndEl.attr('rel')}`);
|
|
|
|
updateIssueHref($selectionEndEl.attr('rel'));
|
|
updateViewGitBlameFragment($selectionEndEl.attr('rel'));
|
|
updateCopyPermalinkUrl($selectionEndEl.attr('rel'));
|
|
}
|
|
|
|
function showLineButton() {
|
|
const menu = document.querySelector('.code-line-menu');
|
|
if (!menu) return;
|
|
|
|
// remove all other line buttons
|
|
for (const el of document.querySelectorAll('.code-line-button')) {
|
|
el.remove();
|
|
}
|
|
|
|
// find active row and add button
|
|
const tr = document.querySelector('.code-view tr.active');
|
|
const td = tr.querySelector('td.lines-num');
|
|
const btn = document.createElement('button');
|
|
btn.classList.add('code-line-button', 'ui', 'basic', 'button');
|
|
btn.innerHTML = svg('octicon-kebab-horizontal');
|
|
td.prepend(btn);
|
|
|
|
// put a copy of the menu back into DOM for the next click
|
|
btn.closest('.code-view').append(menu.cloneNode(true));
|
|
|
|
createTippy(btn, {
|
|
trigger: 'click',
|
|
hideOnClick: true,
|
|
content: menu,
|
|
placement: 'right-start',
|
|
interactive: true,
|
|
onShow: (tippy) => {
|
|
tippy.popper.addEventListener('click', () => {
|
|
tippy.hide();
|
|
}, {once: true});
|
|
},
|
|
});
|
|
}
|
|
|
|
export function initRepoCodeView() {
|
|
if ($('.code-view .lines-num').length > 0) {
|
|
$(document).on('click', '.lines-num span', function (e) {
|
|
const linesEls = getLineEls();
|
|
const selectedEls = Array.from(linesEls).filter((el) => {
|
|
return el.matches(`[rel=${this.getAttribute('id')}]`);
|
|
});
|
|
|
|
let from;
|
|
if (e.shiftKey) {
|
|
from = Array.from(linesEls).filter((el) => {
|
|
return el.closest('tr').classList.contains('active');
|
|
});
|
|
}
|
|
selectRange($(linesEls), $(selectedEls), from ? $(from) : null);
|
|
|
|
if (window.getSelection) {
|
|
window.getSelection().removeAllRanges();
|
|
} else {
|
|
document.selection.empty();
|
|
}
|
|
|
|
showLineButton();
|
|
});
|
|
|
|
$(window).on('hashchange', () => {
|
|
let m = window.location.hash.match(rangeAnchorRegex);
|
|
const $linesEls = $(getLineEls());
|
|
let $first;
|
|
if (m) {
|
|
$first = $linesEls.filter(`[rel=${m[1]}]`);
|
|
if ($first.length) {
|
|
selectRange($linesEls, $first, $linesEls.filter(`[rel=${m[2]}]`));
|
|
|
|
// show code view menu marker (don't show in blame page)
|
|
if (!isBlame()) {
|
|
showLineButton();
|
|
}
|
|
|
|
$('html, body').scrollTop($first.offset().top - 200);
|
|
return;
|
|
}
|
|
}
|
|
m = window.location.hash.match(singleAnchorRegex);
|
|
if (m) {
|
|
$first = $linesEls.filter(`[rel=L${m[2]}]`);
|
|
if ($first.length) {
|
|
selectRange($linesEls, $first);
|
|
|
|
// show code view menu marker (don't show in blame page)
|
|
if (!isBlame()) {
|
|
showLineButton();
|
|
}
|
|
|
|
$('html, body').scrollTop($first.offset().top - 200);
|
|
}
|
|
}
|
|
}).trigger('hashchange');
|
|
}
|
|
$(document).on('click', '.fold-file', ({currentTarget}) => {
|
|
invertFileFolding(currentTarget.closest('.file-content'), currentTarget);
|
|
});
|
|
$(document).on('click', '.copy-line-permalink', async ({currentTarget}) => {
|
|
await clippie(toAbsoluteUrl(currentTarget.getAttribute('data-url')));
|
|
});
|
|
}
|