Telegram github commits and releases
4.44K subscribers
601 files
20.9K links
Broadcast from the most important Telegram clients' repositories
Download Telegram
morethanwords/tweb/master • 1ddddac • 2 files, +39/-2
Fix big emoji disappearing in production builds

An emoji-only message stopped rendering big and animated once built — in dev it
was always fine, which is what made it look like a settings or server problem.

src/vendor/emoji/regex.ts held lone surrogates in its string value: the upstream
pattern splits astral emoji across alternations, so '\uD83C[\uDFFB-\uDFFF]' has
a high surrogate with nothing to pair with. Rolldown folds that constant into
the bundle and re-encodes it as UTF-8, where a lone surrogate has no
representation — in index.worker-*.js every one of them came out as U+FFFD
followed by the leftover "d83c", 1246 times. Evaluating the shipped pattern
confirms it: 😳 🔥 👍 😀 no longer match, while ❤ ☝ ⌚ still do, because those
are BMP and were never written as surrogates.

So the worker's FULL_REG_EXP produced no messageEntityEmoji for any astral
emoji, totalEntities carried none, and bubbles' emojiStrLength === strLength
test could never hold — bigEmojis stayed 0, no .emoji-big, no animated sticker.
Only the worker chunk was affected, which is why the input's own suggestions
kept working: those are parsed on the main thread.

The literal is pure ASCII now — every \u and \x backslash is doubled, so the
escapes stay text and RegExp resolves them itself when the pattern compiles.
Same matching, and nothing left in the string for a bundler to mangle. A test
pins both halves: the source stays ASCII, and parseEntities still returns an
emoji entity spanning the whole emoji. A clean build has zero U+FFFD in any
chunk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

morethanwords/tweb/master • 1d0ec11 • 1 files, +7/-0
Stop rebuilding the emoji suggestions for an emoji already suggested

Typing 😳 into an input that already ended in 😳 tore the suggestion strip down
and built it again from scratch: a fresh searchCustomEmoji, a new container for
every item, replaceWith, and the scroll back at zero — all to arrive at exactly
what was on screen.

checkAutocomplete only guards against an unchanged value, but the emoticon is
derived from it: getCustomEmojiSuggestionEmoticon slices from the emoji entity
that ends at the caret, so a longer value can still name the same emoji.
checkEmoticon took it from there unconditionally.

It returns early now when the emoticon matches the one already on screen. The
helper has to be visible for that — hidden means the strip was torn down and
must be rebuilt, and it also covers an in-flight request that a hide cancelled.
A different emoticon, or a `:query` search (which clears this.emoticon), still
renders as before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

#webk
🫡3
morethanwords/tweb/master • e127825 • 11 files, +297/-1
Show a video chat plate under the topbar for group calls you haven't joined

The topbar already had a plate for RTMP live streams, but an ordinary group
video chat had no entry point besides the header button. Add a sibling plate
laid out like the other clients: icon and title on one side, the join button
on the other, and a three-face preview of who is already in the call centred
on the plate.

The stack is centred with equal-basis side columns rather than absolute
positioning, so it sits on the real centre (as tdesktop paints it at
`width / 2`) while a long call title can never run under it. On handhelds the
stack shrinks so holding the centre does not clip the participants counter.

Visibility follows every other client: `call_active` + `call_not_empty`, the
call is not an RTMP stream (that one belongs to the live plate), and we have
not joined it ourselves. The joined edge comes from the controller's
`instance` event and the closing edge from the instance's own `state` event,
since the controller keeps its reference after a hang-up.

`getGroupCallPreview` gives the plate the call plus the first N participant
peers in one hop, so the whole participants map never crosses the worker
boundary, and only chats that actually have a call subscribe to the full peer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

morethanwords/tweb/master • 558c0f7 • 2 files, +59/-25
Lay the Requests plate out like the translation one

The join-requests plate was the only floating plate still built as a
full-bleed clickable row: faces pinned to the start, title floating in
the middle, close button inside the same ripple area. Give it the shape
every other plate already has — the stretched, centred pill of the
translation plate, with the faces taking the start slot exactly the way
the close button takes the end one, so neither shifts the label off the
plate's centre. The width formula both plates now share moved into
`--pc-centered-button-width`.

Along the way:

- the label never carried `pinned-requests-title`, so its primary colour
and bold weight were dead CSS; it inherits both from the pill now, and
gets `text-overflow-no-wrap` so a large count ellipsizes instead of
wrapping out of the fixed 48px plate;
- the controller reported `height: 52` while the plate has always been
48 CSS px, which made `setFloating` reserve 4px too much;
- avatars go 32 -> 40px, matching the buttons on the plate, and shrink
back on handhelds where the centred label would otherwise run under
them (same trade-off as `.pinned-group-call`);
- the close button is no longer nested inside the clickable area, so
dismissing the plate can't also open the requests tab;
- `<Show>` is keyed and the label is updated from an effect: `set()`
writes a fresh `RequestData` on every `chat_requests` update, and a
plain `Show` kept the previous count and faces on a visible plate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

morethanwords/tweb/master • 5801aa2 • 300 files, +788/-788
Build

#webk
🫡3
morethanwords/tweb/master • 6cd749c • 6 files, +348/-169
Fix stories never opening in production builds

Clicking a story did nothing on the built app while dev was fine — `actions.set`
threw before it could hand the peer to the viewer:

TypeError: Cannot read properties of undefined (reading 'index')
at getNearestStory <- getNearestStories <- checkForNearestSkipped <- set

The logic was right, the bundle was not. `getNearestStory`'s parameter defaults
read `state`, and the minifier bound that read to `ep` — clientPip's module-level
`state`, normally `undefined` — while the body of the very same function kept the
correct name. The source map settles it: minified `ep` maps to store.tsx:133, name
`state`, so one source symbol was emitted under two names. viewer.tsx had it twice
as well, `wasPlaying = !stories.paused` reading `Kd.paused` off a premium-feature
descriptor, so closing any popup resumed a story that was paused.

Both resolve their defaults in the function body now, where the minifier gets the
binding right. rolldown 1.2.3 still miscompiles the old shape, so this is not
something an upgrade fixes.

scripts/check-bundle-mangling.mjs catches the class rather than the pattern, since
the trigger never reproduced in isolation: for every identifier in a default value
— parameter, destructuring, class field — it asks the source map which source
variable it came from and checks how that variable is spelled everywhere else in
the chunk. One dominant spelling plus a stray one is the signature. It finds
exactly these four in the shipped bundle and nothing else, and now runs at the end
of `pnpm build`. It also fails on any U+FFFD, which is how the astral emoji regex
died in the worker chunk.

Vite 8.2.1 comes along: rolldown 1.2.3 no longer rewrites lone surrogates in
folded constants, the trap that one cost us.

`build.cssTarget` is explicit now. `target: 'es2020'` says nothing about browsers,
so lightningcss lowered every logical property into a pair of `:lang()` rules,
emitted after the block they came from and with a higher specificity — a physical
`left` authored next to `inset-inline-*` silently lost, which is what pinned the
emoji panel to the window edge. Naming versions that support logical properties
natively drops 13680 `:lang()` selectors to 1824 and 157 KiB off the CSS. RTL is
unaffected: it runs off `documentElement.dir`, which index.ts sets for every
language.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

morethanwords/tweb/master • e3730e1 • 300 files, +1278/-1325
Build

#webk
🫡3
UnigramDev/Unigram/develop • 00351ee • 2 files, +7/-2
Fix emojis color in inline buttons

UnigramDev/Unigram/develop • 960fa0d • 1 files, +51/-30
Fix reply markup button templates

#unigram
UnigramDev/Unigram/ephemeral-command-null-caption • ecb8bef • 1 files, +1/-1
Fix crash when sending non-text content with ephemeral bot commands

CreateSendMessage maps anything that is not an InputMessageText to a null
caption, then dereferenced it unconditionally. Sending a sticker (or any
other media) in a chat where HasEphemeralBotCommands is set crashed with a
NullReferenceException.

Reported by crash telemetry on 12.9.0.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
🫡2
UnigramDev/Unigram/translate-font-errors • 72085fa • 1 files, +17/-0
Translate font and render target error messages

DirectWrite's two font-file errors were missing from TranslateText entirely, so
every locale reported them as its own crash group. The render target message was
already translated, but only for a few locales.

Adds the localized variants observed in crash reports:

- font file not found: de, es, fr, it, pt, ru, tr, zh-Hans
- font file access denied: fr, ru
- wrong render target: es, fr, ru

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/gallery-convertof-null • 64b9ee6 • 1 files, +6/-0
Fix crash when opening the gallery

ConvertOf is an x:Bind function binding over ViewModel.SelectedItem, and
Bindings.Update runs from Load before an item has been selected, so the very
first evaluation dereferenced a null GalleryMedia.

ConvertCaption in the same file already guards its input the same way.

Reported by crash telemetry on 12.9.0.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/search-tabs-negative-index • a2af3cb • 1 files, +3/-1
Fix crash when searching while no tab is selected

Query guarded the upper bound of SelectedTab but not the lower. SelectedTab is
bound two-way to the tab selector's SelectedIndex, which is -1 whenever there is
no selection, so Tabs[SelectedTab] threw ArgumentOutOfRangeException if the tabs
were repopulated while the user was still typing.

Reported by crash telemetry on 12.9.0.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/fasttext-fontsize-zero • f2a73c6 • 1 files, +9/-1
Fix crash when rendering the wallpaper preview bubbles

The plain-run fast path wrote whatever font size had changed, including 0, and
XAML rejects a FontSize of 0 with E_INVALIDARG. GetOrCreateRun already treats
non-positive as "inherit" and clears the property instead; the fast path now
does the same.

MessageBubble.Mockup reaches this: it calls SetText without a font size, so the
default of 0 replaces the size the bubble was previously rendered with, and the
transition is exactly what triggers the write.

Reported by crash telemetry on 12.9.0.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/fasttext-fontsize-zero • 8dfbb0a • 1 files, +3/-9
Use the computed font size in the fast path

Per review: _fontSize is the raw value, fontSize is what AutoFontSize resolved
it to, and the slow path already passes the resolved one to GetOrCreateRun. The
fast path writing the raw value is the actual defect, so write the resolved one
instead of clearing the property — clearing would inherit the parent's size
rather than applying the theme size.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/copytext-null-formattedtext • e0fae49 • 1 files, +8/-0
Fix crash when copying an empty selection or an absent note

CopyText(XamlRoot, FormattedText) dereferenced its argument immediately, but its
callers pass values that are legitimately null: TextSelectionManager's
GetSelectedText returns null when nothing is selected, BlockQuote passes the
result of FormattedTextBlock.GetSelectedText, and ProfileHeader passes
UserFullInfo.Note, which is null when the contact has no note.

The method is async void, so the NullReferenceException was posted to the
dispatcher and terminated the app instead of being catchable.

Reported by crash telemetry on 12.9.0.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/settingsexpander-collapse-guard • ecf9d06 • 1 files, +3/-1
Fix the expander never collapsing its content

The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.

Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/settingspanel-negative-height • 52187f9 • 1 files, +5/-1
Never report a negative desired height from SettingsPanel

The IsHeader branch subtracts the trailing gap that follows the last child, but
the accumulator starts at 0 when IsFooter is also set, so a panel with nothing
visible reported -16. XAML rejects a negative desired size.

Both flags are set together on ShareGroupCallPopup, ChatInviteLinkInfoPopup and
FolderPage, so the combination is reachable; no crash report is currently tied
to it, this is a latent defect found while reading the layout code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/voipcall-log-on-change • 7547111 • 1 files, +2/-2
Only log the voip ready state when it changes

OnStateUpdated logged before the guard that ignores an unchanged state, so a
source repeating the same state filled the whole log window with identical
lines — one crash report held nothing but Failed, every entry stamped in the
same millisecond, which cost us everything that preceded it.

OnSignalBarsUpdated just below already logs from inside its change guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/develop • 5b03668 • 3 files, +3/-17
Enable rich text editor for all users

UnigramDev/Unigram/develop • e23f722 • 1 files, +4/-0
Accessibility names for AI and rich text editor

UnigramDev/Unigram/develop • daf6021 • 1 files, +25/-13
Fix time entity paste

UnigramDev/Unigram/develop • 4743674 • 2 files, +146/-71
Align date entity parsing to TDLib

UnigramDev/Unigram/develop • 276ef92 • 2 files, +49/-36
Optimize reply markup content

UnigramDev/Unigram/develop • fd2b0c5 • 6 files, +18/-8
Fix reply markup buttons accessibility

UnigramDev/Unigram/develop • 15d6521 • 1 files, +18/-11
Fix double click to select

UnigramDev/Unigram/develop • f108380 • 1 files, +17/-2
Announce call duration on message focus

The automation name for a call message was only the outcome, so a screen reader
read "Incoming call" and nothing more. The duration was rendered by CallContent
into a TextBlock of its own, reachable only by navigating into the message, so
it was never part of what is read when the message takes focus.

Both GetSummary overloads share a helper rather than repeating the missed check
that ToOutcomeText and CallContent already make separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 73568f3 • 1 files, +1/-1
Fix reply in closed topics

UnigramDev/Unigram/develop • 1622cf7 • 6 files, +2612/-2585
Move service message text rendering to MessageServiceText

MessageService was 3252 lines: a 600 line control, and 2600 lines of static
text builders that the chat list, the reply preview and the automation peers
call without ever touching the control.

Pure move, no behaviour change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • f9bda35 • 1 files, +1/-1
Fix ContentOpacity default value

The property is a double but was registered with a boxed int, so the CLR
getter's cast throws for anything that reads it before it's set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/develop • cc87fde • 1 files, +3/-39
Reuse UpdateMessageTopic from UpdateContent

The MessageHeaderMessageTopic branch was a verbatim copy of it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • d5ca61b • 2 files, +23/-0
Recycle service message containers

Only MessageSelector was recycled, so a service message leaving the viewport
went back on the queue still holding its view model and its text inlines.

Also gives the per-content subclasses a place to reset the state they set,
which the shared templates need.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • be84f01 • 1 files, +16/-0
Fix stale publisher chip on recycled gift messages

MessageGift, MessageGiftedPremium, MessageGiftedStars and MessagePremiumGiftCode
share one template, but only the first one touches the publisher border, so a
recycled container kept showing the previous message's publisher.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • e2d8ffb • 1 files, +7/-9
Start a selection only on a direct hit

ResolvePosition resolves a point to the nearest selectable, clamping when the
pointer is past the end of a line, below the last block or beside a cell. A drag
needs that clamp, but a press was taking it too: pressing on media resolved to a
neighbouring caption, and the capture that follows moved the whole pointer
sequence to the selection root. Nothing else saw it, so a drag out of
PhotoContent never began.

The press now requires ResolvePosition to report a direct hit. Drags still clamp,
so extending a selection past the end of a line is unchanged.

The multi-tap branch tested the same flag to keep gap presses out of the tap
sequence, which the anchor now guarantees.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 6df1637 • 1 files, +24/-6
Fix null dereferences in service message text

- a gift purchase offer in a chat that isn't a user
- a stopped poll event whose message isn't a poll
- a refunded suggested post whose replied-to message has no post info
- an empty task list on a checklist update

All of them dereferenced without checking; they now fall back to no text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • d715db8 • 1 files, +18/-13
Guard the entity offset in the suggested photo action

The bold entity was placed at the index of {0} in the format string, with no
check: a translation without the placeholder gave a negative offset.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/develop • a1bed19 • 1 files, +7/-5
Use ActionRequestedPeerUser when no user was shared

The branch that shows it sat behind a condition identical to the one above it,
so it was unreachable and an unresolved user list rendered as a gap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 84f8968 • 1 files, +20/-3
Keep entities aligned across a placeholder substitution

Only entities starting after the placeholder were shifted, so an entity
spanning it (markdown parsed before the substitution) kept its old length and
ended short. ReplaceWithLinks also measured against the last inserted name
rather than the placeholder, leaving anything in between unshifted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 0220eb9 • 1 files, +5/-21
Remove dead code in service message text

An if (true) with an unreachable else, a condition on a local just set to 0,
and a local assigned but never read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 87b706c • 2 files, +9/-0
Expose ExecuteServiceMessage on the message delegate

Service message controls moving out of ChatView.xaml can no longer reach the
view's click handler, so they need the action through their own delegate.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 8b6dc02 • 5 files, +369/-331
Move the gift service message into its own control

126 lines of ChatView.xaml and four branches of UpdateContent, addressed by
string through FindName, become one control with typed fields.

The publisher chip and the ribbon are now reset in one place shared by the
three contents that don't own them, and Recycle releases the sticker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 3bae444 • 5 files, +251/-231
Move the upgraded gift service message into its own control

Same shape as the gift one; the dead commented-out layout it carried is gone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • e9b50de • 5 files, +116/-57
Move the giveaway prize service message into its own control

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 06c45d7 • 5 files, +186/-139
Move the suggested birthdate service message into its own control

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/develop • 1d8c026 • 5 files, +123/-84
Move the chat background service message into its own control

Covers the event log counterpart too, which shares the template.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 606cb04 • 6 files, +205/-165
Move the photo service message into its own control

Chat photo, suggested profile photo and story mention share one template; the
story-opening click moves with them, so ChatView no longer reaches into the
template by name to find the segments to animate from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 3d4814f • 8 files, +80/-145
Move the unsupported message into its own control

Unsupported messages have been rendered by the service template for a while, so
the old Content\UnsupportedContent control was only still reachable through the
bubble content factory. Its update check, the one part still in use, moves with
the template.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 8b4907d • 10 files, +342/-243
Move the header service messages into their own controls

The account info and message topic headers were the last two contents
UpdateContent knew about, so the base implementation is now empty and
MessageService no longer reaches into a template it doesn't own.

UpdateServiceWithForumTopic dropped its action parameter with them: it existed
to call UpdateMessageTopic, which is now the header control's own method.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • e634d66 • 1 files, +38/-7
Allow a selection to start from the bubble surface

Requiring a direct hit kept media drags working but took the gap with it: a press
in the padding beside the text, which ResolvePosition clamps to the nearest
block, no longer began a selection.

The press is now filtered by what it landed on rather than by whether the
position was clamped. Walking up from the pressed element to the root, a
selectable starts a gesture, any other IContent refuses it, and reaching the root
without crossing either means the press was on the bubble itself. Testing the
root by identity would not do: a press on the bubble background reports whichever
template element carries the brush, never the root the manager was attached to.

The multi-tap branch tests the clamp again, since clamped presses can begin a
gesture once more and a click beside the text should not turn the next one into a
word selection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • fb2e4b3 • 1 files, +1/-1
Update TDLib

#unigram
UnigramDev/Unigram/develop • 953756e • 1 files, +1/-1
Fix crash when sending non-text content with ephemeral bot commands (#3323)

CreateSendMessage maps anything that is not an InputMessageText to a null
caption, then dereferenced it unconditionally. Sending a sticker (or any
other media) in a chat where HasEphemeralBotCommands is set crashed with a
NullReferenceException.

Reported by crash telemetry on 12.9.0.0.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • f254228 • 1 files, +17/-0
Translate font and render target error messages (#3324)

DirectWrite's two font-file errors were missing from TranslateText entirely, so
every locale reported them as its own crash group. The render target message was
already translated, but only for a few locales.

Adds the localized variants observed in crash reports:

- font file not found: de, es, fr, it, pt, ru, tr, zh-Hans
- font file access denied: fr, ru
- wrong render target: es, fr, ru

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 0d1ab88 • 1 files, +6/-0
Fix crash when opening the gallery (#3326)

ConvertOf is an x:Bind function binding over ViewModel.SelectedItem, and
Bindings.Update runs from Load before an item has been selected, so the very
first evaluation dereferenced a null GalleryMedia.

ConvertCaption in the same file already guards its input the same way.

Reported by crash telemetry on 12.9.0.0.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • 1088776 • 1 files, +3/-1
Fix crash when rendering the wallpaper preview bubbles (#3327)

* Fix crash when rendering the wallpaper preview bubbles

The plain-run fast path wrote whatever font size had changed, including 0, and
XAML rejects a FontSize of 0 with E_INVALIDARG. GetOrCreateRun already treats
non-positive as "inherit" and clears the property instead; the fast path now
does the same.

MessageBubble.Mockup reaches this: it calls SetText without a font size, so the
default of 0 replaces the size the bubble was previously rendered with, and the
transition is exactly what triggers the write.

Reported by crash telemetry on 12.9.0.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Use the computed font size in the fast path

Per review: _fontSize is the raw value, fontSize is what AutoFontSize resolved
it to, and the slow path already passes the resolved one to GetOrCreateRun. The
fast path writing the raw value is the actual defect, so write the resolved one
instead of clearing the property — clearing would inherit the parent's size
rather than applying the theme size.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram
UnigramDev/Unigram/develop • 50f25b1 • 1 files, +5/-1
Never report a negative desired height from SettingsPanel (#3328)

The IsHeader branch subtracts the trailing gap that follows the last child, but
the accumulator starts at 0 when IsFooter is also set, so a panel with nothing
visible reported -16. XAML rejects a negative desired size.

Both flags are set together on ShareGroupCallPopup, ChatInviteLinkInfoPopup and
FolderPage, so the combination is reachable; no crash report is currently tied
to it, this is a latent defect found while reading the layout code.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • afb36b1 • 1 files, +3/-1
Fix the expander never collapsing its content (#3329)

The scoped batch's Completed handler compares _tracker against a value captured
with a post-increment, so the comparison was (n + 1) == n and never held. The
handler is what applies Visibility.Collapsed after the collapse animation, and
PopupRoot.Visibility is set to Visible unconditionally just above it, so
collapsed content stayed visible and kept participating in layout — hidden only
by PopupHost.Height = 0 and the negative margin.

Pre-increment gives the guard its intended meaning: apply the final visibility
unless a newer toggle has started since this batch began.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • e032aae • 1 files, +8/-0
Fix crash when copying an empty selection or an absent note (#3330)

CopyText(XamlRoot, FormattedText) dereferenced its argument immediately, but its
callers pass values that are legitimately null: TextSelectionManager's
GetSelectedText returns null when nothing is selected, BlockQuote passes the
result of FormattedTextBlock.GetSelectedText, and ProfileHeader passes
UserFullInfo.Note, which is null when the contact has no note.

The method is async void, so the NullReferenceException was posted to the
dispatcher and terminated the app instead of being catchable.

Reported by crash telemetry on 12.9.0.0.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

UnigramDev/Unigram/develop • e5fe62d • 1 files, +2/-2
Only log the voip ready state when it changes (#3331)

OnStateUpdated logged before the guard that ignores an unchanged state, so a
source repeating the same state filled the whole log window with identical
lines — one crash report held nothing but Failed, every entry stamped in the
same millisecond, which cost us everything that preceded it.

OnSignalBarsUpdated just below already logs from inside its change guard.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

#unigram