my website at ewancroft.uk
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: correct mention rendering with multi-byte UTF-8 characters

The AT Protocol uses UTF-8 byte indices for facets, but JavaScript's
String.slice() operates on UTF-16 code units. This caused mentions to
shift when multi-byte characters (emojis, accented chars) appeared
before them.

Now using TextEncoder/TextDecoder to properly handle byte-level
slicing for accurate facet positioning.

+23 -5
+23 -5
src/lib/components/layout/main/card/BlueskyPostCard.svelte
··· 83 83 if (!facets || facets.length === 0) return escapeHtml(text); 84 84 const sortedFacets = [...facets].sort((a, b) => a.index.byteStart - b.index.byteStart); 85 85 86 + // Convert text to UTF-8 bytes for proper facet indexing 87 + const encoder = new TextEncoder(); 88 + const decoder = new TextDecoder(); 89 + const bytes = encoder.encode(text); 90 + 86 91 let result = ''; 87 - let lastIndex = 0; 92 + let lastByteIndex = 0; 88 93 89 94 for (const facet of sortedFacets) { 90 95 const { byteStart, byteEnd } = facet.index; 91 - result += escapeHtml(text.slice(lastIndex, byteStart)); 92 - const facetText = text.slice(byteStart, byteEnd); 96 + 97 + // Extract text before facet 98 + if (lastByteIndex < byteStart) { 99 + const beforeBytes = bytes.slice(lastByteIndex, byteStart); 100 + result += escapeHtml(decoder.decode(beforeBytes)); 101 + } 102 + 103 + // Extract facet text 104 + const facetBytes = bytes.slice(byteStart, byteEnd); 105 + const facetText = decoder.decode(facetBytes); 93 106 const feature = facet.features?.[0]; 94 107 95 108 if (feature) { ··· 106 119 result += escapeHtml(facetText); 107 120 } 108 121 109 - lastIndex = byteEnd; 122 + lastByteIndex = byteEnd; 123 + } 124 + 125 + // Add remaining text after last facet 126 + if (lastByteIndex < bytes.length) { 127 + const remainingBytes = bytes.slice(lastByteIndex); 128 + result += escapeHtml(decoder.decode(remainingBytes)); 110 129 } 111 130 112 - result += escapeHtml(text.slice(lastIndex)); 113 131 return result; 114 132 } 115 133