返回 JoyAI-Echo
format.ts
1 import i18n, { currentLocale } from "@/i18n";
2
3 /** Truncate the first user message into a chat title. */
4 export function deriveTitle(preview: string | undefined, fallback: string): string {
5 if (!preview) return fallback;
6 const oneLine = preview.replace(/\s+/g, " ").trim();
7 if (!oneLine) return fallback;
8 return oneLine.length > 60 ? `${oneLine.slice(0, 57)}…` : oneLine;
9 }
10
11 /** Loose ISO-or-epoch parser; returns ``null`` for missing/invalid input. */
12 function parseDate(value: string | number | null | undefined): Date | null {
13 if (value === null || value === undefined || value === "") return null;
14 const d = new Date(value);
15 return Number.isNaN(d.getTime()) ? null : d;
16 }
17
18 const RELATIVE_THRESHOLDS: [number, Intl.RelativeTimeFormatUnit][] = [
19 [60, "second"],
20 [60, "minute"],
21 [24, "hour"],
22 [7, "day"],
23 [4.345, "week"],
24 [12, "month"],
25 [Number.POSITIVE_INFINITY, "year"],
26 ];
27
28 const relativeTimeFormatters = new Map<string, Intl.RelativeTimeFormat>();
29 const dateTimeFormatters = new Map<string, Intl.DateTimeFormat>();
30
31 function activeLocale(locale?: string): string {
32 return locale || i18n.resolvedLanguage || i18n.language || currentLocale();
33 }
34
35 function relativeTimeFormatter(locale: string): Intl.RelativeTimeFormat {
36 const existing = relativeTimeFormatters.get(locale);
37 if (existing) return existing;
38 const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
39 relativeTimeFormatters.set(locale, formatter);
40 return formatter;
41 }
42
43 function dateTimeFormatter(locale: string): Intl.DateTimeFormat {
44 const existing = dateTimeFormatters.get(locale);
45 if (existing) return existing;
46 const formatter = new Intl.DateTimeFormat(locale, {
47 dateStyle: "medium",
48 timeStyle: "short",
49 });
50 dateTimeFormatters.set(locale, formatter);
51 return formatter;
52 }
53
54 export function relativeTime(
55 value: string | number | null | undefined,
56 locale?: string,
57 ): string {
58 const date = parseDate(value);
59 if (!date) return "";
60 let delta = (date.getTime() - Date.now()) / 1000;
61 const formatter = relativeTimeFormatter(activeLocale(locale));
62 for (const [step, unit] of RELATIVE_THRESHOLDS) {
63 if (Math.abs(delta) < step) {
64 return formatter.format(Math.round(delta), unit);
65 }
66 delta /= step;
67 }
68 return formatter.format(Math.round(delta), "year");
69 }
70
71 export function fmtDateTime(
72 value: string | number | null | undefined,
73 locale?: string,
74 ): string {
75 const date = parseDate(value);
76 return date ? dateTimeFormatter(activeLocale(locale)).format(date) : "";
77 }
78
78 lines TYPESCRIPT