diff --git a/bskyembed/src/components/post.tsx b/bskyembed/src/components/post.tsx index 7ed7439403..e607f10104 100644 --- a/bskyembed/src/components/post.tsx +++ b/bskyembed/src/components/post.tsx @@ -1,4 +1,4 @@ -import {h} from 'preact' +import {ComponentChild, h} from 'preact' import logo from '../../assets/logo_full_name.svg' import {Like as LikeIcon} from '../icons/Like' @@ -7,6 +7,7 @@ import {Repost as RepostIcon} from '../icons/Repost' import {Robot as RobotIcon} from '../icons/Robot' import {CONTENT_LABELS} from '../labels' import * as app from '../lexicons/app' +import {FacetRenderer} from '../util/facet-renderer' import {niceDate} from '../util/nice-date' import {prettyNumber} from '../util/pretty-number' import {getRkey} from '../util/rkey' @@ -146,65 +147,51 @@ function PostContent({record}: {record: app.bsky.feed.post.Main | null}) { // render an empty
that adds an extra flex gap above the embed if (!record?.text) return null - // const rt = new RichText({ - // text: record.text, - // facets: record.facets, - // }) + const rt = new FacetRenderer({text: record.text, facets: record.facets}) + const richText: ComponentChild[] = [] - // const richText = [] + let counter = 0 + for (const segment of rt.segments()) { + if (segment.link) { + richText.push( + + {segment.text} + , + ) + } else if (segment.mention) { + richText.push( + + {segment.text} + , + ) + } else if (segment.tag) { + richText.push( + + {segment.text} + , + ) + } else { + richText.push(segment.text) + } + counter++ + } - // let counter = 0 - // for (const segment of rt.segments()) { - // if ( - // segment.link && - // app.bsky.richtext.facet.link.$safeValidate(segment.link).success - // ) { - // richText.push( - // - // {segment.text} - // , - // ) - // } else if ( - // segment.mention && - // app.bsky.richtext.facet.mention.safeValidate(segment.mention).success - // ) { - // richText.push( - // - // {segment.text} - // , - // ) - // } else if ( - // segment.tag && - // app.bsky.richtext.facet.tag.safeValidate(segment.tag).success - // ) { - // richText.push( - // - // {segment.text} - // , - // ) - // } else { - // richText.push(segment.text) - // } - - // counter++ - // } - - // return ( - //
- // {richText} - //
- // ) + return ( ++ {richText} +
+ ) } diff --git a/bskyembed/src/util/facet-renderer.ts b/bskyembed/src/util/facet-renderer.ts new file mode 100644 index 0000000000..de3d2757dc --- /dev/null +++ b/bskyembed/src/util/facet-renderer.ts @@ -0,0 +1,87 @@ +import * as facetDefs from '../lexicons/app/bsky/richtext/facet.defs' + +type Facet = facetDefs.Main +type Link = facetDefs.Link +type Mention = facetDefs.Mention +type Tag = facetDefs.Tag + +const encoder = new TextEncoder() +const decoder = new TextDecoder() + +export class FacetSegment { + constructor( + public text: string, + public facet?: Facet, + ) {} + + get link(): Link | undefined { + return this.facet?.features.find( + f => f.$type === 'app.bsky.richtext.facet#link', + ) as Link | undefined + } + + get mention(): Mention | undefined { + return this.facet?.features.find( + f => f.$type === 'app.bsky.richtext.facet#mention', + ) as Mention | undefined + } + + get tag(): Tag | undefined { + return this.facet?.features.find( + f => f.$type === 'app.bsky.richtext.facet#tag', + ) as Tag | undefined + } +} + +export class FacetRenderer { + private utf8: Uint8Array + private facets: Facet[] + + constructor({text, facets}: {text: string; facets?: Facet[]}) { + this.utf8 = encoder.encode(text) + this.facets = (facets ?? []) + .filter(f => f.index.byteStart <= f.index.byteEnd) + .sort((a, b) => a.index.byteStart - b.index.byteStart) + } + + private slice(start: number, end: number): string { + return decoder.decode(this.utf8.slice(start, end)) + } + + *segments(): Generator++ ++ #tangled is everything I wanted from a modern #git forge: - + #atproto based (this is HUGE!) - European - Decentralized - + Federated - Social with #vouching - Built with #golang and using a + #permissive #MIT license What not to like there?
+ — perpetual screaming πΊπ¦ (@bazub.zubko.cc) + 10 May 2026 at 21:43 +
[image or embed] +