appendBlueskyText
Append Bluesky text to this AnnotatedString.Builder and invoke onFacet for every valid facet feature, sorted by byteStart ascending.
This is the lowest-level primitive in the Compose helper. The full text is appended to the builder once, up front. Callers handle styling, inline content, link/string annotations themselves inside the onFacet lambda — this primitive's job is only to translate UTF-8 byte offsets (the facet contract) into UTF-16 char offsets (the AnnotatedString contract) correctly.
The byte-to-char mapping is computed once via a one-pass walker (see Utf8CharBoundaryTable) and shared across all facets, so this function is O(text.length + facets.size · log codepoints) and allocates no transient Strings for boundary lookups.
Silent skip contract. Malformed facets are dropped without throwing:
byteStart < 0,byteEnd <= byteStart, orbyteEnd > utf8 lengthbyte offsets that fall inside a codepoint's UTF-8 byte sequence
facets with an empty
featureslist
Unknown feature variants (FacetFeaturesUnion.Unknown) are not dropped here — they are passed to onFacet so callers can decide whether to render them or skip them. The higher-level buildBlueskyAnnotatedString skips unknowns silently.
Sort order guarantee. onFacet is invoked in byteStart ascending order. Ties between facets at the same byteStart preserve their original order in the input list (stable sort). Ties between features inside a single facet preserve their features array order. This lets callers stream output (e.g. appendInlineContent) without sorting themselves.
The slice parameter on onFacet is text.substring(startChar, endChar), precomputed so callers building inline content (icon next to a @handle, ellipsized URLs) don't need to re-substring with their own bounds.
Parameters
the post's plain text. Appended to the builder up front.
facet annotations from the post record (may be null).
invoked for each facet feature, with the typed sealed-parent FacetFeaturesUnion, the translated UTF-16 char range [startChar, endChar), and the matched substring slice. Runs with the builder as receiver — call addStyle, addLink, addStringAnnotation, etc. to attach behavior to the range.