mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-11 05:35:42 +02:00
Parse `Co-authored-by:` trailers from commit messages and surface contributors as an avatar stack across the commit page, commits list, PR commits tab, latest-commit row, blame, graph, and dashboard feed. - Up to 10 visible 20px avatars, GitHub-style overlap (6px first stride, 4px between subsequent), `+N` chip for the rest. - Label: 1 → name; 2 → `<a> and <b>`; 3+ → `<N> people` opens a Tippy popup with all participants. - Names and avatars link to the repo's commits-by-author search; fall back to profile or `mailto:`. - Trailer parsing uses `net/mail.ParseAddress`, scans only the trailing paragraph, filters out the commit's own author/committer. - Drops the non-standard `Co-committed-by:` emission on squash merge and web edits. Devtest: `/devtest/coauthor-avatars`. Fixes #25521 ---- <img width="353" height="277" alt="image" src="https://github.com/user-attachments/assets/72092ceb-97ca-4b09-9557-0b72d3c5458e" /> <img width="533" height="328" src="https://github.com/user-attachments/assets/11d0c8f8-8b3f-4f2e-9993-879f1c06bcc5" /> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Giteabot <teabot@gitea.io>
53 lines
1.8 KiB
TypeScript
53 lines
1.8 KiB
TypeScript
import {createTippy} from '../modules/tippy.ts';
|
|
import {toggleElem} from '../utils/dom.ts';
|
|
import {registerGlobalEventFunc, registerGlobalInitFunc} from '../modules/observer.ts';
|
|
|
|
export function initRepoEllipsisButton() {
|
|
registerGlobalEventFunc('click', 'onRepoEllipsisButtonClick', async (el: HTMLInputElement, e: Event) => {
|
|
e.preventDefault();
|
|
const expanded = el.getAttribute('aria-expanded') === 'true';
|
|
toggleElem(el.parentElement!.querySelector('.commit-body')!);
|
|
el.setAttribute('aria-expanded', String(!expanded));
|
|
});
|
|
}
|
|
|
|
export function initCommitStatuses() {
|
|
registerGlobalInitFunc('initCommitStatuses', (el: HTMLElement) => {
|
|
const nextEl = el.nextElementSibling!;
|
|
if (!nextEl.matches('.tippy-target')) throw new Error('Expected next element to be a tippy target');
|
|
createTippy(el, {
|
|
content: nextEl,
|
|
placement: 'bottom-start',
|
|
interactive: true,
|
|
role: 'dialog',
|
|
theme: 'box-with-header',
|
|
});
|
|
});
|
|
}
|
|
|
|
export function initAvatarStackPopup() {
|
|
registerGlobalInitFunc('initAvatarStackPopup', (el: HTMLElement) => {
|
|
const nextEl = el.nextElementSibling!;
|
|
if (!nextEl.matches('.tippy-target')) throw new Error('Expected next element to be a tippy target');
|
|
createTippy(el, {
|
|
content: nextEl,
|
|
placement: 'bottom-start',
|
|
interactive: true,
|
|
role: 'dialog',
|
|
theme: 'menu',
|
|
trigger: 'click',
|
|
hideOnClick: true,
|
|
});
|
|
});
|
|
}
|
|
|
|
export function initCommitFileHistoryFollowRename() {
|
|
registerGlobalInitFunc('initCommitHistoryFollowRename', (el: HTMLInputElement) => {
|
|
el.addEventListener('change', () => {
|
|
const url = new URL(window.location.toString());
|
|
url.searchParams.set('follow-rename', `${el.checked}`);
|
|
window.location.assign(url.toString());
|
|
});
|
|
});
|
|
}
|