at://Press
1export function formatDate(iso: string, opts?: { weekday?: boolean }): string {
2 const options: Intl.DateTimeFormatOptions = {
3 year: "numeric",
4 month: "long",
5 day: "numeric",
6 };
7 if (opts?.weekday) options.weekday = "long";
8 return new Date(iso).toLocaleDateString("en-US", options);
9}
10
11import { DEFAULT_EXCERPT_LENGTH } from "./constants";
12
13export function excerpt(content: string, maxLen = DEFAULT_EXCERPT_LENGTH): string {
14 const plain = content
15 .replace(/^#{1,6}\s+/gm, "")
16 .replace(/\*{1,2}([^*]+)\*{1,2}/g, "$1")
17 .replace(/`([^`]+)`/g, "$1")
18 .replace(/~~~[\s\S]*?~~~/g, "")
19 .replace(/\n+/g, " ")
20 .trim();
21 if (plain.length <= maxLen) return plain;
22 return plain.slice(0, maxLen).replace(/\s+\S*$/, "") + "\u2026";
23}
24
25export function relativeTime(iso: string): string {
26 const seconds = Math.floor((Date.now() - new Date(iso).getTime()) / 1000);
27 if (seconds < 60) return "just now";
28 const minutes = Math.floor(seconds / 60);
29 if (minutes < 60) return `${minutes}m`;
30 const hours = Math.floor(minutes / 60);
31 if (hours < 24) return `${hours}h`;
32 const days = Math.floor(hours / 24);
33 if (days < 30) return `${days}d`;
34 return formatDate(iso);
35}
36
37export function bskyPostUrl(uri: string, handle: string): string {
38 // at://did:plc:.../app.bsky.feed.post/rkey -> https://bsky.app/profile/handle/post/rkey
39 const rkey = uri.split("/").pop() || "";
40 return `https://bsky.app/profile/${handle}/post/${rkey}`;
41}
42
43export function escapeXml(s: string): string {
44 return s
45 .replace(/&/g, "&")
46 .replace(/</g, "<")
47 .replace(/>/g, ">")
48 .replace(/"/g, """)
49 .replace(/'/g, "'");
50}