import { escapeHtml, raw, type Raw } from './html.ts';
import type { PlatformId } from '../types.ts';
/** Where an @mention or #hashtag points on each platform. */
const PROFILE_BASE: Record = {
x: 'https://x.com/',
threads: 'https://www.threads.com/@',
instagram: 'https://www.instagram.com/',
tiktok: 'https://www.tiktok.com/@',
bluesky: 'https://bsky.app/profile/',
reddit: 'https://www.reddit.com/user/',
};
const TAG_BASE: Partial> = {
x: 'https://x.com/hashtag/',
instagram: 'https://www.instagram.com/explore/tags/',
tiktok: 'https://www.tiktok.com/tag/',
};
const TOKEN = /(https?:\/\/[^\s<]+)|(^|\s)(@[\w.-]+)|(^|\s)(#[\w]+)/g;
function link(href: string, label: string): string {
return `${escapeHtml(label)}`;
}
/**
* Turn post text into markup: escape everything, then make links, mentions
* and hashtags clickable back to the platform they came from.
*/
export function linkify(text: string, platform: PlatformId): Raw {
let out = '';
let cursor = 0;
for (const match of text.matchAll(TOKEN)) {
const index = match.index;
out += escapeHtml(text.slice(cursor, index));
const [whole, url, mentionLead, mention, tagLead, tag] = match;
if (url) {
out += link(url, url.replace(/^https?:\/\/(www\.)?/, ''));
} else if (mention) {
out += escapeHtml(mentionLead ?? '') + link(PROFILE_BASE[platform] + mention.slice(1), mention);
} else if (tag) {
const base = TAG_BASE[platform];
out += escapeHtml(tagLead ?? '') + (base ? link(base + tag.slice(1), tag) : escapeHtml(tag));
} else {
out += escapeHtml(whole);
}
cursor = index + whole.length;
}
out += escapeHtml(text.slice(cursor));
return raw(out.replace(/\n/g, '
'));
}