| 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; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| 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}`; |
| } |
|
|