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, '');
}