a tool for shared writing and social publishing
0
fork

Configure Feed

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

new simpler facet addition algo

+49 -119
+49 -119
app/lish/[did]/[publication]/[rkey]/TextBlock.tsx
··· 17 17 let facets = [...(props.facets || [])]; 18 18 if (!props.preview) { 19 19 for (let highlight of highlights) { 20 - facets = addFacet(facets, { 21 - index: { 22 - byteStart: highlight.startOffset 23 - ? new UnicodeString( 24 - props.plaintext.slice(0, highlight.startOffset), 25 - ).length 26 - : 0, 27 - byteEnd: new UnicodeString( 28 - props.plaintext.slice(0, highlight.endOffset ?? undefined), 29 - ).length, 30 - }, 31 - features: [ 32 - { $type: "pub.leaflet.richtext.facet#highlight" }, 33 - { 34 - $type: "pub.leaflet.richtext.facet#id", 35 - id: `${props.index.join(".")}_${highlight.startOffset || 0}`, 20 + facets = addFacet( 21 + facets, 22 + { 23 + index: { 24 + byteStart: highlight.startOffset 25 + ? new UnicodeString( 26 + props.plaintext.slice(0, highlight.startOffset), 27 + ).length 28 + : 0, 29 + byteEnd: new UnicodeString( 30 + props.plaintext.slice(0, highlight.endOffset ?? undefined), 31 + ).length, 36 32 }, 37 - ], 38 - }); 33 + features: [ 34 + { $type: "pub.leaflet.richtext.facet#highlight" }, 35 + { 36 + $type: "pub.leaflet.richtext.facet#id", 37 + id: `${props.index.join(".")}_${highlight.startOffset || 0}`, 38 + }, 39 + ], 40 + }, 41 + new UnicodeString(props.plaintext).length, 42 + ); 39 43 } 40 44 } 41 45 return new RichText({ text: props.plaintext, facets }); ··· 153 157 } 154 158 } 155 159 } 156 - 157 - function addFacet(facets: Facet[], facet: Facet) { 158 - // Handle empty facets array 160 + function addFacet(facets: Facet[], newFacet: Facet, length: number) { 159 161 if (facets.length === 0) { 160 - return [facet]; 162 + return [newFacet]; 161 163 } 162 164 163 - const newStart = facet.index.byteStart; 164 - const newEnd = facet.index.byteEnd; 165 - 166 - // Find facets that overlap with the new facet 167 - const overlapping: number[] = []; 168 - for (let i = 0; i < facets.length; i++) { 169 - const existing = facets[i]; 170 - const existingStart = existing.index.byteStart; 171 - const existingEnd = existing.index.byteEnd; 172 - 173 - // Check if there's any overlap 174 - if (newEnd > existingStart && newStart < existingEnd) { 175 - overlapping.push(i); 176 - } 177 - } 165 + const allFacets = [...facets, newFacet]; 178 166 179 - // No overlaps - just insert the new facet at the right position 180 - if (overlapping.length === 0) { 181 - // Find the correct position to insert (maintain sorted order) 182 - let insertIndex = facets.length; 183 - for (let i = 0; i < facets.length; i++) { 184 - if (newStart < facets[i].index.byteStart) { 185 - insertIndex = i; 186 - break; 187 - } 188 - } 167 + // Collect all boundary positions 168 + const boundaries = new Set<number>(); 169 + boundaries.add(0); 170 + boundaries.add(length); 189 171 190 - let newFacets = [...facets]; 191 - newFacets.splice(insertIndex, 0, facet); 192 - return newFacets; 172 + for (const facet of allFacets) { 173 + boundaries.add(facet.index.byteStart); 174 + boundaries.add(facet.index.byteEnd); 193 175 } 194 176 195 - // Handle overlaps by splitting and merging 196 - const newFacets: Facet[] = []; 197 - let processedUpTo = 0; 198 - 199 - for (let i = 0; i < facets.length; i++) { 200 - const existing = facets[i]; 201 - const existingStart = existing.index.byteStart; 202 - const existingEnd = existing.index.byteEnd; 203 - 204 - // Add non-overlapping facets before the current one 205 - if (!overlapping.includes(i)) { 206 - newFacets.push(existing); 207 - continue; 208 - } 209 - 210 - // Split the existing facet based on overlap with new facet 211 - const overlapStart = Math.max(existingStart, newStart); 212 - const overlapEnd = Math.min(existingEnd, newEnd); 213 - 214 - // Part before overlap 215 - if (existingStart < overlapStart) { 216 - newFacets.push({ 217 - index: { 218 - byteStart: existingStart, 219 - byteEnd: overlapStart, 220 - }, 221 - features: [...existing.features], 222 - }); 223 - } 177 + const sortedBoundaries = Array.from(boundaries).sort((a, b) => a - b); 178 + const result: Facet[] = []; 224 179 225 - // Overlapping part - merge features 226 - newFacets.push({ 227 - index: { 228 - byteStart: overlapStart, 229 - byteEnd: overlapEnd, 230 - }, 231 - features: [...existing.features, ...facet.features], 232 - }); 180 + // Process segments between consecutive boundaries 181 + for (let i = 0; i < sortedBoundaries.length - 1; i++) { 182 + const start = sortedBoundaries[i]; 183 + const end = sortedBoundaries[i + 1]; 233 184 234 - // Part after overlap 235 - if (overlapEnd < existingEnd) { 236 - newFacets.push({ 237 - index: { 238 - byteStart: overlapEnd, 239 - byteEnd: existingEnd, 240 - }, 241 - features: [...existing.features], 242 - }); 243 - } 185 + // Find facets that are active at the start position 186 + const activeFacets = allFacets.filter( 187 + (facet) => facet.index.byteStart <= start && facet.index.byteEnd > start, 188 + ); 244 189 245 - // Track what part of the new facet we've processed 246 - if (newStart < overlapStart && processedUpTo < overlapStart) { 247 - // Add the part of new facet before this overlap 248 - newFacets.push({ 249 - index: { 250 - byteStart: newStart, 251 - byteEnd: overlapStart, 252 - }, 253 - features: [...facet.features], 190 + // Only create facet if there are active facets (features present) 191 + if (activeFacets.length > 0) { 192 + const features = activeFacets.flatMap((f) => f.features); 193 + result.push({ 194 + index: { byteStart: start, byteEnd: end }, 195 + features, 254 196 }); 255 197 } 256 - processedUpTo = overlapEnd; 257 198 } 258 199 259 - // Add any remaining part of the new facet after all overlaps 260 - if (processedUpTo < newEnd) { 261 - newFacets.push({ 262 - index: { 263 - byteStart: processedUpTo, 264 - byteEnd: newEnd, 265 - }, 266 - features: [...facet.features], 267 - }); 268 - } 269 - 270 - return newFacets.sort((a, b) => a.index.byteStart - b.index.byteStart); 200 + return result; 271 201 }