spb/social-runtime-crawler
Public
TypeScript 91.8%
HTML 3.2%
JavaScript 3%
SQL 1.4%
CSS 0.7%
1export function fmtNum(n: number | string | null | undefined, digits = 0): string {2 if (n === null || n === undefined || n === "") return "—";3 const v = Number(n);4 if (Number.isNaN(v)) return String(n);5 if (Math.abs(v) >= 1e9) return (v / 1e9).toFixed(1) + "B";6 if (Math.abs(v) >= 1e6) return (v / 1e6).toFixed(1) + "M";7 if (Math.abs(v) >= 1e4) return (v / 1e3).toFixed(1) + "k";8 return v.toLocaleString("en-US", { maximumFractionDigits: digits });9}1011export function fmtPct(n: number | null | undefined, digits = 0): string {12 if (n === null || n === undefined || Number.isNaN(Number(n))) return "—";13 return `${(Number(n) * 100).toFixed(digits)}%`;14}1516export function fmtDuration(s: number | null | undefined): string {17 if (!s && s !== 0) return "—";18 const v = Math.round(Number(s));19 const h = Math.floor(v / 3600);20 const m = Math.floor((v % 3600) / 60);21 const sec = v % 60;22 return h ? `${h}:${String(m).padStart(2, "0")}:${String(sec).padStart(2, "0")}` : `${m}:${String(sec).padStart(2, "0")}`;23}2425export function fmtTime(ts: string | Date | null | undefined): string {26 if (!ts) return "—";27 const d = new Date(ts);28 return d.toLocaleTimeString("en-CA", { hour12: false });29}3031export function fmtDate(ts: string | Date | null | undefined): string {32 if (!ts) return "—";33 const d = new Date(ts);34 return d.toLocaleString("en-CA", { hour12: false, month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" });35}3637export function ago(ts: string | Date | null | undefined): string {38 if (!ts) return "—";39 const diff = (Date.now() - new Date(ts).getTime()) / 1000;40 if (diff < 60) return `${Math.max(0, Math.round(diff))}s ago`;41 if (diff < 3600) return `${Math.round(diff / 60)}m ago`;42 if (diff < 86400) return `${Math.round(diff / 3600)}h ago`;43 return `${Math.round(diff / 86400)}d ago`;44}4546export function truncate(s: string | null | undefined, n: number): string {47 if (!s) return "";48 return s.length > n ? s.slice(0, n - 1) + "…" : s;49}5051export const TYPE_COLORS: Record<string, string> = {52 video: "#38e1ff",53 channel: "#9d7bff",54 profile: "#9d7bff",55 person: "#9d7bff",56 post: "#ffb347",57 comment: "#ff7ad9",58 community: "#43e69a",59 page: "#43e69a",60 hashtag: "#6b7a8c",61 image: "#38e1ff",62 organization: "#43e69a",63};6465export const EVENT_COLORS: Record<string, string> = {66 ACTION_PLANNED: "#ffb347",67 ACTION_EXECUTED: "#43e69a",68 ACTION_FAILED: "#ff5c7a",69 PAGE_OPENED: "#38e1ff",70 NETWORK_SCHEMA_DISCOVERED: "#9d7bff",71 CONNECTOR_PATTERN_LEARNED: "#9d7bff",72 CONNECTOR_DEGRADED: "#ff5c7a",73 CONNECTOR_REPAIRED: "#43e69a",74 LOOP_DETECTED: "#ff5c7a",75 AUTH_REQUIRED: "#ff5c7a",76 MEDIA_DISCOVERED: "#ff7ad9",77 VIDEO_DISCOVERED: "#38e1ff",78 POST_DISCOVERED: "#ffb347",79 PROFILE_DISCOVERED: "#9d7bff",80};8182export function eventColor(t: string): string {83 return EVENT_COLORS[t] ?? "#6b7a8c";84}85