this repo has no description
atproto
bluesky
typescript
express
1interface Facet {
2 index: { byteStart: number; byteEnd: number };
3 features: Array<
4 | { $type: 'app.bsky.richtext.facet#mention' }
5 | { $type: 'app.bsky.richtext.facet#link'; uri: string }
6 | { $type: 'app.bsky.richtext.facet#tag' }
7 >;
8}
9
10export function renderTextWithFacets(text: string, facets: Facet[]): string {
11 if (!facets || facets.length === 0) return text;
12
13 // Convert string to a Uint8Array (UTF-8 encoded bytes)
14 const encoder = new TextEncoder();
15 const decoder = new TextDecoder();
16 const textBytes = encoder.encode(text);
17
18 let result = '';
19 let lastIndex = 0;
20
21 facets.forEach((facet) => {
22 const { byteStart, byteEnd } = facet.index;
23
24 // Extract parts of the text using byte offsets
25 const preFacetText = decoder.decode(textBytes.slice(lastIndex, byteStart));
26 const facetText = decoder.decode(textBytes.slice(byteStart, byteEnd));
27
28 let replacedFacetText = facetText;
29
30 facet.features.forEach((feature) => {
31 if (feature.$type === 'app.bsky.richtext.facet#mention') {
32 replacedFacetText = `<a href="../profile/${facetText.replace('@', '')}" class="mention">${facetText}</a>`;
33 }
34
35 if (feature.$type === 'app.bsky.richtext.facet#link') {
36 replacedFacetText = `<a href="${feature.uri}" target="_blank" rel="nofollow">${facetText}</a>`;
37 }
38
39 if (feature.$type === 'app.bsky.richtext.facet#tag') {
40 replacedFacetText = `<a href="https://bsky.app/hashtag/${facetText.replace('#', '')}" class="hashtag">${facetText}</a>`; //TODO: Replace with search endpoint when added
41 }
42 });
43
44 result += preFacetText + replacedFacetText;
45 lastIndex = byteEnd;
46 });
47
48 // Append any remaining text after the last facet
49 result += decoder.decode(textBytes.slice(lastIndex));
50
51 return result;
52}