import type { Facet } from './types'; export function escapeHtml(str: string): string { return str .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } export function renderFacets(text: string, facets?: Facet[]): string { if (!facets?.length) return escapeHtml(text); const encoder = new TextEncoder(); const decoder = new TextDecoder(); const bytes = encoder.encode(text); const sorted = [...facets].sort((a, b) => a.index.byteStart - b.index.byteStart); let result = ''; let lastEnd = 0; for (const facet of sorted) { const { byteStart, byteEnd } = facet.index; result += escapeHtml(decoder.decode(bytes.slice(lastEnd, byteStart))); const facetText = escapeHtml(decoder.decode(bytes.slice(byteStart, byteEnd))); const feature = facet.features?.[0]; if (feature?.$type === 'app.bsky.richtext.facet#link') { result += `${facetText}`; } else if (feature?.$type === 'app.bsky.richtext.facet#mention') { result += `${facetText}`; } else if (feature?.$type === 'app.bsky.richtext.facet#tag') { result += `${facetText}`; } else { result += facetText; } lastEnd = byteEnd; } result += escapeHtml(decoder.decode(bytes.slice(lastEnd))); return result; } export function timeAgo(dateStr: string): string { const now = Date.now(); const then = new Date(dateStr).getTime(); const diff = now - then; const mins = Math.floor(diff / 60000); const hours = Math.floor(diff / 3600000); const days = Math.floor(diff / 86400000); if (mins < 1) return 'just now'; if (mins < 60) return `${mins}m`; if (hours < 24) return `${hours}h`; if (days < 7) return `${days}d`; return new Date(dateStr).toLocaleDateString('en-US', { month: 'short', day: 'numeric', ...(days > 365 ? { year: 'numeric' } : {}), }); } export function formatDate(dateStr: string): string { return new Date(dateStr).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', }); } export function formatDuration(seconds: number): string { const m = Math.floor(seconds / 60); const s = Math.floor(seconds % 60); return `${m}:${s.toString().padStart(2, '0')}`; } export function formatCount(n: number): string { if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M'; if (n >= 10000) return (n / 1000).toFixed(0) + 'K'; if (n >= 1000) return (n / 1000).toFixed(1) + 'K'; return String(n || 0); } export function slugify(str: string): string { return str .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-|-$/g, ''); }