feat: vendor gitea 1.16.2

This commit is contained in:
2026-06-02 08:36:01 +00:00
parent 247cd60f69
commit 54798b4615
2145 changed files with 162965 additions and 126447 deletions
+41 -25
View File
@@ -23,10 +23,18 @@ export function extname(path: string): string {
}
/** test whether a variable is an object */
export function isObject(obj: any): boolean {
export function isObject<T = Record<string, any>>(obj: any): obj is T {
return Object.prototype.toString.call(obj) === '[object Object]';
}
/** Whether the current platform is macOS or iOS. */
export const isMac = /Mac/i.test(navigator.userAgent);
/** Platform-aware display symbols for keyboard modifier and special keys. */
export const keySymbols: Record<string, string> = isMac ?
{Mod: '⌘', Alt: '⌥', Shift: '⇧', Ctrl: '⌃', Up: '↑', Down: '↓', Enter: '⏎'} :
{Mod: 'Ctrl', Shift: 'Shift', Alt: 'Alt', Up: '↑', Down: '↓', Enter: '⏎'};
/** returns whether a dark theme is enabled */
export function isDarkTheme(): boolean {
const style = window.getComputedStyle(document.documentElement);
@@ -35,7 +43,12 @@ export function isDarkTheme(): boolean {
/** strip <tags> from a string */
export function stripTags(text: string): string {
return text.replace(/<[^>]*>?/g, '');
let prev = '';
while (prev !== text) {
prev = text;
text = text.replace(/<[^>]*>?/g, '');
}
return text;
}
export function parseIssueHref(href: string): IssuePathInfo {
@@ -55,9 +68,9 @@ export function parseRepoOwnerPathInfo(pathname: string): RepoOwnerPathInfo {
export function parseIssuePageInfo(): IssuePageInfo {
const el = document.querySelector('#issue-page-info');
return {
issueNumber: parseInt(el?.getAttribute('data-issue-index')),
issueNumber: parseInt(el?.getAttribute('data-issue-index') || ''),
issueDependencySearchType: el?.getAttribute('data-issue-dependency-search-type') || '',
repoId: parseInt(el?.getAttribute('data-issue-repo-id')),
repoId: parseInt(el?.getAttribute('data-issue-repo-id') || ''),
repoLink: el?.getAttribute('data-issue-repo-link') || '',
};
}
@@ -88,14 +101,18 @@ export function blobToDataURI(blob: Blob): Promise<string> {
try {
const reader = new FileReader();
reader.addEventListener('load', (e) => {
resolve(e.target.result as string);
if (e.target) {
resolve(e.target.result as string);
} else {
reject(new Error('blobToDataURI: FileReader failed'));
}
});
reader.addEventListener('error', () => {
reject(new Error('FileReader failed'));
reject(new Error('blobToDataURI: FileReader error'));
});
reader.readAsDataURL(blob);
} catch (err) {
reject(err);
} catch (err: unknown) {
reject(err instanceof Error ? err : new Error(String(err)));
}
});
}
@@ -111,21 +128,22 @@ export function convertImage(blob: Blob, mime: string): Promise<Blob> {
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
const context = canvas.getContext('2d');
if (!context) return reject(new Error('convertImage: no context'));
context.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
if (!(blob instanceof Blob)) return reject(new Error('imageBlobToPng failed'));
if (!(blob instanceof Blob)) return reject(new Error('convertImage: toBlob failed'));
resolve(blob);
}, mime);
} catch (err) {
reject(err);
} catch (err: unknown) {
reject(err instanceof Error ? err : new Error(String(err)));
}
});
img.addEventListener('error', () => {
reject(new Error('imageBlobToPng failed'));
reject(new Error('convertImage: image failed to load'));
});
img.src = await blobToDataURI(blob);
} catch (err) {
reject(err);
} catch (err: unknown) {
reject(err instanceof Error ? err : new Error(String(err)));
}
});
}
@@ -174,27 +192,25 @@ export function sleep(ms: number): Promise<void> {
}
export function isImageFile({name, type}: {name?: string, type?: string}): boolean {
return /\.(avif|jpe?g|png|gif|webp|svg|heic)$/i.test(name || '') || type?.startsWith('image/');
return Boolean(/\.(avif|jpe?g|png|gif|webp|svg|heic)$/i.test(name || '') || type?.startsWith('image/'));
}
export function isVideoFile({name, type}: {name?: string, type?: string}): boolean {
return /\.(mpe?g|mp4|mkv|webm)$/i.test(name || '') || type?.startsWith('video/');
return Boolean(/\.(mpe?g|mp4|mkv|webm)$/i.test(name || '') || type?.startsWith('video/'));
}
export function toggleFullScreen(fullscreenElementsSelector: string, isFullScreen: boolean, sourceParentSelector?: string): void {
export function toggleFullScreen(fullScreenEl: HTMLElement, isFullScreen: boolean, sourceParentSelector?: string): void {
// hide other elements
const headerEl = document.querySelector('#navbar');
const contentEl = document.querySelector('.page-content');
const footerEl = document.querySelector('.page-footer');
const headerEl = document.querySelector('#navbar')!;
const contentEl = document.querySelector('.page-content')!;
const footerEl = document.querySelector('.page-footer')!;
toggleElem(headerEl, !isFullScreen);
toggleElem(contentEl, !isFullScreen);
toggleElem(footerEl, !isFullScreen);
const sourceParentEl = sourceParentSelector ? document.querySelector(sourceParentSelector) : contentEl;
const fullScreenEl = document.querySelector(fullscreenElementsSelector);
const outerEl = document.querySelector('.full.height');
toggleElemClass(fullscreenElementsSelector, 'fullscreen', isFullScreen);
const sourceParentEl = sourceParentSelector ? document.querySelector(sourceParentSelector)! : contentEl;
const outerEl = document.querySelector('.full.height')!;
toggleElemClass(fullScreenEl, 'fullscreen', isFullScreen);
if (isFullScreen) {
outerEl.append(fullScreenEl);
} else {