mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-23 05:42:33 +09:00
- Adds a copy button to each action step header that copies the step's rendered log output to clipboard. - Extract a shared `copyToClipboard(target, content)` helper in `clipboard.ts` that adds SVG success/failure feedback. - `is-loading` height for the new helper is sourced from `--loading-size`. - Change actions log timestamp format to include seconds. The indented-markdown code-block fix has moved to #37748. <img width="244" height="165" alt="copystep" src="https://github.com/user-attachments/assets/ce286b51-f77b-4d82-b161-ca0aa7ec4fdc" /> <img width="187" height="150" alt="copybt" src="https://github.com/user-attachments/assets/5366b290-b776-496d-8dd4-58d5fa60be92" /> Fixes: https://github.com/go-gitea/gitea/issues/26116 --- This PR was written with the help of Claude Opus 4.7 --------- Signed-off-by: silverwind <me@silverwind.io> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> Co-authored-by: Nicolas <bircni@icloud.com>
91 lines
2.7 KiB
TypeScript
91 lines
2.7 KiB
TypeScript
import dayjs from 'dayjs';
|
|
import utc from 'dayjs/plugin/utc.js';
|
|
import {getCurrentLocale} from '../utils.ts';
|
|
import type {ConfigType} from 'dayjs';
|
|
|
|
dayjs.extend(utc);
|
|
|
|
/**
|
|
* Returns an array of millisecond-timestamps of start-of-week days (Sundays)
|
|
*
|
|
* @param startDate The start date. Can take any type that dayjs accepts.
|
|
* @param endDate The end date. Can take any type that dayjs accepts.
|
|
*/
|
|
export function startDaysBetween(startDate: ConfigType, endDate: ConfigType): number[] {
|
|
const start = dayjs.utc(startDate);
|
|
const end = dayjs.utc(endDate);
|
|
|
|
let current = start;
|
|
|
|
// Ensure the start date is a Sunday
|
|
while (current.day() !== 0) {
|
|
current = current.add(1, 'day');
|
|
}
|
|
|
|
const startDays: number[] = [];
|
|
while (current.isBefore(end)) {
|
|
startDays.push(current.valueOf());
|
|
current = current.add(1, 'week');
|
|
}
|
|
|
|
return startDays;
|
|
}
|
|
|
|
export function firstStartDateAfterDate(inputDate: Date): number {
|
|
if (!(inputDate instanceof Date)) {
|
|
throw new Error('Invalid date');
|
|
}
|
|
const dayOfWeek = inputDate.getUTCDay();
|
|
const daysUntilSunday = 7 - dayOfWeek;
|
|
const resultDate = new Date(inputDate.getTime());
|
|
resultDate.setUTCDate(resultDate.getUTCDate() + daysUntilSunday);
|
|
return resultDate.valueOf();
|
|
}
|
|
|
|
export type DayData = {
|
|
week: number,
|
|
additions: number,
|
|
deletions: number,
|
|
commits: number,
|
|
};
|
|
|
|
export type DayDataObject = {
|
|
[timestamp: string]: DayData,
|
|
};
|
|
|
|
export function fillEmptyStartDaysWithZeroes(startDays: number[], data: DayDataObject): DayData[] {
|
|
const result: Record<string, any> = {};
|
|
|
|
for (const startDay of startDays) {
|
|
result[startDay] = data[startDay] || {'week': startDay, 'additions': 0, 'deletions': 0, 'commits': 0};
|
|
}
|
|
|
|
return Object.values(result);
|
|
}
|
|
|
|
let dateFormat: Intl.DateTimeFormat;
|
|
|
|
// ISO 8601 UTC with 7-digit fractional seconds, matching Go's `2006-01-02T15:04:05.0000000Z07:00`
|
|
export function formatDatetimeISO(unixSeconds: number): string {
|
|
const base = new Date(unixSeconds * 1000).toISOString().slice(0, 19);
|
|
const frac = unixSeconds - Math.floor(unixSeconds);
|
|
const fracInt = Math.floor(frac * 10_000_000);
|
|
return `${base}.${String(fracInt).padStart(7, '0')}Z`;
|
|
}
|
|
|
|
/** Format a Date to a localized format, for example "21 May 2026, 14:30:45". */
|
|
export function formatDatetime(date: Date | number): string {
|
|
if (!dateFormat) {
|
|
dateFormat = new Intl.DateTimeFormat(getCurrentLocale(), {
|
|
day: '2-digit',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
hour: '2-digit',
|
|
hourCycle: new Intl.DateTimeFormat([], {hour: 'numeric'}).resolvedOptions().hourCycle === 'h12' ? 'h12' : 'h23',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
});
|
|
}
|
|
return dateFormat.format(date);
|
|
}
|