UnigramDev/Unigram/formatted-text-block-dates • b6cc86e • 3 files, +347/-6
Anchor rendered indices to the paragraph a block starts at
The fast path left _indexMap null on the grounds that a plain single run maps
1:1 onto StyledText offsets. That only holds for a block whose first paragraph
is the message's first: MessageTextBlock gives every plain paragraph sandwiched
between typed ones its own block, and those render a slice of a shared
StyledText starting somewhere in the middle.
With no map, the converters returned the rendered index unchanged, so copying a
selection from such a block returned text from the start of the message, word
and paragraph expansion resolved against the wrong paragraph, and the search
highlight landed past the end of the block.
_origin is set beside _first/_last so it is correct before every early return
and on both paths - the slow path can also end up with an empty map when every
entity is dropped by the length guard, and that takes the same fallback.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/formatted-text-block-dates • 7b39d50 • 2 files, +24/-12
Give the code block tokenization an identity that does not restart
_templateExecuted was doing two jobs: the "text has been applied" flag OnLoaded
tests, and the token that ProcessCodeBlock compares against after its await.
OnUnloaded resets it, so the second job was an ABA - execution 1 is handed out
again after every unload, and a tokenization started before it resumes, matches,
and calls ClearCollection on inlines whose Paragraph is by then back in the
shared pool, wiping whichever block dequeued it.
The async guard now uses its own never-reset counter. That leaves the flag as a
flag, so it is a bool.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/formatted-text-block-dates • ecaf440 • 7 files, +34/-22
Build the monospace font chain once, in Theme
Nine sites spelled out "Cascadia Mono, Consolas" and built a FontFamily from it.
FormattedTextBlock meant to memoize its own, but the local it returned was never
assigned, so it allocated one per monospace entity - and ProcessCodeBlock, being
recursive, one per node of the token tree. Six of the others sit inside a loop
over entities, so they allocated one per code span they rendered.
Theme already owns XamlAutoFontFamily and rebuilds the families that embed it in
UpdateEmojiSet, which is the only place the chain can change, so the family
belongs there and everything reads Theme.Current.MonospaceFontFamily.
Those six spelled the chain without the fallback tail, and now inherit it: a
character the monospace faces don't cover renders through the text and emoji
fonts instead of the system default.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Post #20703
41