File size: 4,185 Bytes
f0b240d 7e7e06a 0b2fee5 7e7e06a e535f52 7e7e06a e535f52 7e7e06a e535f52 7e7e06a e535f52 7e7e06a e535f52 7e7e06a e535f52 7e7e06a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | export function toxicityColor(probability: number): string {
const pct = probability * 100;
if (pct >= 70) return "#ff4e45";
if (pct >= 40) return "#f5a623";
return "#2ba640";
}
export function formatPct(probability: number): string {
return `${Math.round(probability * 100)}%`;
}
export function newId(): string {
return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
}
export const TEAM_MEMBERS = [
"Roberto Molero",
"Mirae Kang",
"Jonathan Brasales",
"Andrés Torrez",
] as const;
export function randomTeamMember(): string {
return TEAM_MEMBERS[Math.floor(Math.random() * TEAM_MEMBERS.length)];
}
const RTF = new Intl.RelativeTimeFormat("es", { numeric: "auto" });
const TIME_UNITS: Array<[Intl.RelativeTimeFormatUnit, number]> = [
["year", 60 * 60 * 24 * 365],
["month", 60 * 60 * 24 * 30],
["day", 60 * 60 * 24],
["hour", 60 * 60],
["minute", 60],
["second", 1],
];
export function relativeTime(input: string | number | Date): string {
const date = input instanceof Date ? input : new Date(input);
const seconds = (date.getTime() - Date.now()) / 1000;
if (!Number.isFinite(seconds)) return "";
for (const [unit, secondsInUnit] of TIME_UNITS) {
if (Math.abs(seconds) >= secondsInUnit || unit === "second") {
return RTF.format(Math.round(seconds / secondsInUnit), unit);
}
}
return "";
}
export function truncate(text: string, max = 140): string {
if (text.length <= max) return text;
return `${text.slice(0, max - 1).trimEnd()}…`;
}
const ADJECTIVES = [
"happy", "lucky", "wild", "calm", "bright", "swift", "quiet", "lazy",
"brave", "fuzzy", "shiny", "sleepy", "quick", "noble", "loud", "humble",
"stormy", "sunny", "rainy", "frosty", "spicy", "salty", "sweet", "crazy",
"mighty", "silent", "epic", "cosmic", "rusty", "neon", "wandering", "lone",
"electric", "atomic", "midnight", "golden", "silver", "crimson", "violet", "azure",
"ancient", "feral", "tidal", "lunar", "solar", "ember", "frost", "iron",
"velvet", "wicked", "groovy", "funky", "chill", "vibrant", "moody", "savage",
"tropical", "alpine", "urban", "rogue", "stellar", "nomadic", "boreal", "obsidian",
];
const NOUNS = [
"panda", "tiger", "falcon", "otter", "eagle", "wolf", "fox", "bear",
"lynx", "shark", "raven", "viper", "phoenix", "dragon", "jaguar", "koala",
"moose", "lion", "owl", "octopus", "rabbit", "hawk", "badger", "robin",
"cosmonaut", "drifter", "ninja", "wizard", "rider", "pilot", "skater", "gamer",
"barista", "hacker", "painter", "poet", "sailor", "ranger", "archer", "dancer",
"chef", "cyclist", "writer", "drummer", "diver", "climber", "biker", "florist",
"comet", "nebula", "nova", "pulsar", "quasar", "asteroid", "meteor", "orbiter",
"knight", "monk", "scribe", "merchant", "wanderer", "voyager", "explorer", "pirate",
];
const SUFFIXES = [
"", "", "", "", "",
"_", "_x", "_yt", "_hd", "_real", "_official", "_tv", "_live", "_xd", "_v2",
"99", "01", "07", "23", "42",
"_irl", "_pro",
];
function hashString(str: string): number {
let h = 0x811c9dc5;
for (let i = 0; i < str.length; i++) {
h ^= str.charCodeAt(i);
h = Math.imul(h, 0x01000193) >>> 0;
}
return h >>> 0;
}
function mix(h: number, salt: number): number {
let x = (h ^ Math.imul(salt + 1, 0x85ebca6b)) >>> 0;
x = Math.imul(x ^ (x >>> 13), 0xc2b2ae35) >>> 0;
return (x ^ (x >>> 16)) >>> 0;
}
/**
* Generate a YouTube-style random username, deterministic per seed.
* Three independent hash mixes pick adjective, noun, and suffix so that
* seeds that share a prefix (e.g. `yt-VIDEOID-0`, `yt-VIDEOID-1`) still
* produce visibly different usernames.
*/
export function randomUsername(seed: string | number | undefined | null): string {
const str = seed == null ? "anon" : String(seed);
const base = hashString(str);
const adj = ADJECTIVES[mix(base, 1) % ADJECTIVES.length] ?? "anon";
const noun = NOUNS[mix(base, 2) % NOUNS.length] ?? "user";
const suffix = SUFFIXES[mix(base, 3) % SUFFIXES.length] ?? "";
const needsNum = suffix === "" || suffix.endsWith("_");
const num = needsNum ? String(mix(base, 4) % 1000) : "";
return `${adj}_${noun}${suffix}${num}`;
}
|