Dark Mode

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 0f3eb43

Browse files
andcommitted
1 parent f952f21 commit 0f3eb43

File tree

1 file changed

+56
-2
lines changed
  • bluesky-thread.html

1 file changed

+56
-2
lines changed

bluesky-thread.html

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,60 @@

Bluesky Thread Viewer

325325
return posts;
326326
}
327327

328+
// Render text with facets (links, mentions, etc.)
329+
function renderTextWithFacets(text, facets) {
330+
if (!facets || !facets.length) {
331+
return document.createTextNode(text);
332+
}
333+
334+
// Convert string to bytes for accurate indexing
335+
const encoder = new TextEncoder();
336+
const decoder = new TextDecoder();
337+
const bytes = encoder.encode(text);
338+
339+
// Sort facets by start index
340+
const sorted = [...facets].sort((a, b) => a.index.byteStart - b.index.byteStart);
341+
342+
const container = document.createDocumentFragment();
343+
let lastEnd = 0;
344+
345+
for (const facet of sorted) {
346+
const { byteStart, byteEnd } = facet.index;
347+
348+
// Add text before this facet
349+
if (byteStart > lastEnd) {
350+
const before = decoder.decode(bytes.slice(lastEnd, byteStart));
351+
container.appendChild(document.createTextNode(before));
352+
}
353+
354+
// Get the facet text
355+
const facetText = decoder.decode(bytes.slice(byteStart, byteEnd));
356+
357+
// Check for link feature
358+
const linkFeature = facet.features.find(f => f.$type === 'app.bsky.richtext.facet#link');
359+
if (linkFeature) {
360+
const link = document.createElement('a');
361+
link.href = linkFeature.uri;
362+
link.target = '_blank';
363+
link.textContent = facetText;
364+
container.appendChild(link);
365+
} else {
366+
// For other facet types (mentions, tags), just render as text for now
367+
container.appendChild(document.createTextNode(facetText));
368+
}
369+
370+
lastEnd = byteEnd;
371+
}
372+
373+
// Add remaining text after last facet
374+
if (lastEnd < bytes.length) {
375+
const after = decoder.decode(bytes.slice(lastEnd));
376+
container.appendChild(document.createTextNode(after));
377+
}
378+
379+
return container;
380+
}
381+
328382
// Image modal setup
329383
const imageModal = document.getElementById('imageModal');
330384
const modalImg = imageModal.querySelector('img');
@@ -520,7 +574,7 @@

Bluesky Thread Viewer

520574
metaEl.appendChild(link);
521575
const textEl = document.createElement('div');
522576
textEl.className = 'text';
523-
textEl.textContent = item.post.record.text;
577+
textEl.appendChild(renderTextWithFacets(item.post.record.text, item.post.record.facets));
524578
el.append(authorEl, metaEl, textEl);
525579
// Render embed if present
526580
if (item.post.embed) {
@@ -575,7 +629,7 @@

Bluesky Thread Viewer

575629
metaEl.appendChild(link);
576630
const textEl = document.createElement('div');
577631
textEl.className = 'text';
578-
textEl.textContent = item.post.record.text;
632+
textEl.appendChild(renderTextWithFacets(item.post.record.text, item.post.record.facets));
579633
el.append(authorEl, metaEl, textEl);
580634
// Render embed if present
581635
if (item.post.embed) {

0 commit comments

Comments
(0)