Telegram github commits and releases
4.43K subscribers
601 files
21K links
Broadcast from the most important Telegram clients' repositories
Download Telegram
telegramdesktop/tdesktop/dev • 0233582 • 1 files, +1/-1
Fix build with MSVC.

#tdesktop
🫡3
New telegramdesktop/tdesktop release: v7.0.9 (stable)

- Add saving rich messages to HTML and importing them back.
- Add custom prompt tab to AI editing in the article editor.
- Add experimental Vulkan rendering option on Linux.
- Crash fixes.

#tdesktop
🫡3
telegramdesktop/tdesktop/dev • 90ca232 • 8 files, +304/-18
Added external task entries to downloads manager.

telegramdesktop/tdesktop/dev • 2e457a8 • 11 files, +2440/-0
Added rich message saving to HTML.

telegramdesktop/tdesktop/dev • e177d7f • 8 files, +603/-31
Added rich message import from exported HTML.

telegramdesktop/tdesktop/dev • 3a03bd7 • 1 files, +59/-28
Added regrouping of mixed media selections in rich message editor.

telegramdesktop/tdesktop/dev • 8b54d62 • 1 files, +10/-0
Added group type switch to grouped media context menu.

telegramdesktop/tdesktop/dev • 5f50566 • 3 files, +89/-0
Added ungrouping of grouped media to selection context menu.

telegramdesktop/tdesktop/dev • 580a6e5 • 1 files, +5/-1
Fixed userpic flicker in mentions autocomplete on every typed character.

telegramdesktop/tdesktop/dev • 33f38da • 2 files, +13/-2
Fixed clipped collapsed badge for large counts in emoji list widget.

telegramdesktop/tdesktop/dev • 9e2ff91 • 8 files, +173/-17
Added keep aspect ratio toggle to sticker creation editor.

telegramdesktop/tdesktop/dev • 40751b4 • 1 files, +1/-1
Fix libvpx build with nasm.

telegramdesktop/tdesktop/dev • 91303da • 6 files, +89/-44
Fix possible crash in miniapps.

telegramdesktop/tdesktop/dev • f931072 • 2 files, +3/-1
Fix build with MSVC.

telegramdesktop/tdesktop/dev • e45d60d • 6 files, +105/-30
Add some hardenings to blocking popups.

telegramdesktop/tdesktop/dev • 3ec91b5 • 2 files, +0/-14
Fix build with GCC.

#tdesktop
🫡3
telegramdesktop/tdesktop/dev • a1e89e1 • 6 files, +22/-15
Version 7.0.9.

- Add saving rich messages to HTML and importing them back.
- Add custom prompt tab to AI editing in the article editor.
- Add experimental Vulkan rendering option on Linux.
- Crash fixes.

#tdesktop
🫡3
UnigramDev/Unigram/develop • ebe5e84 • 1 files, +16/-8
fix: Update Shortcuts.md (#3193)

* fix: Update shortcuts

- Add missing formatting options;
- Reordered shortcuts to be in the same order, as they appear in the application;
- Add consistency (spaces between keys and `+` symbols) for shortcuts;

* increase rowspan

#unigram
🫡2
UnigramDev/Unigram/crash-message-selector-null-content-root • abd63ce • 1 files, +40/-5
Fix crash when MessageSelector loads before its content is realized

MessageSelector.OnLoaded built the TextSelectionManager unconditionally:

_textSelectionManager ??= new TextSelectionManager(this, ContentTemplateRoot);

TextSelectionManager's constructor rejects a null root, so whenever a
container was connected before its ContentPresenter had realized the content,
ContentTemplateRoot was still null and the constructor threw
ArgumentNullException out of a Loaded handler, which is unhandled and takes
the app down.

Reported by crash telemetry on 12.8.2 ("Value cannot be null. Parameter
name: root"). The reported log tails show it under heavy UI churn — chat
navigation with popups opening — which is when layout is most likely to lag
behind connection.

Attach as soon as a root is actually available instead: on load when the
content is already realized, otherwise on the first arrange, by which point
the presenter has always produced one. Selection is therefore attached within
the same layout pass and nothing is lost.

The arrange path is guarded by a field null check, so once attached it costs
one comparison and never allocates.

EnsureTextSelectionManager also checks IsConnected. Only OnUnloaded detaches
the manager, so attaching while disconnected would leave the pointer handlers
on the content root permanently and, through them, root the control for the
rest of the session.

The four selection accessors dereferenced the manager unconditionally, which
would have turned this window into a NullReferenceException instead; they are
now null-safe.

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

#unigram
🫡2💅1
UnigramDev/Unigram/crash-message-selector-null-content-root • 15b9841 • 1 files, +49/-5
Fix crash when MessageSelector loads before its content is set

MessageSelector.OnLoaded built the TextSelectionManager unconditionally:

_textSelectionManager ??= new TextSelectionManager(this, ContentTemplateRoot);

TextSelectionManager's constructor rejects a null root, so whenever a
container was connected before it had been given content, the constructor
threw ArgumentNullException out of a Loaded handler, which is unhandled and
takes the app down.

Reported by crash telemetry on 12.8.2 ("Value cannot be null. Parameter
name: root"). The reported log tails show it under heavy UI churn — chat
navigation with popups opening — which is when layout is most likely to lag
behind connection.

Attach whenever a content element is actually present instead: on load, and
again from OnContentChanged whenever the content itself changes. The control
is always handed a UIElement directly and is never initialized through a
ContentTemplate, so Content is the element to attach to and ContentTemplateRoot
was the wrong property to read in the first place.

OnContentChanged detaches first. The manager's pointer handlers live on the
previous content element, so they have to come off before attaching to the
new one, or they outlive the element they were added to and the manager keeps
reporting selection for content that is no longer displayed.

EnsureTextSelectionManager also checks IsConnected. Only OnUnloaded and
OnContentChanged detach the manager, so attaching while disconnected would
leave the pointer handlers on the content permanently and, through them, root
the control for the rest of the session.

The four selection accessors dereferenced the manager unconditionally, which
would have turned this window into a NullReferenceException instead; they are
now null-safe.

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

#unigram
🫡2
UnigramDev/Unigram/develop • b844676 • 1 files, +49/-5
Fix crash when MessageSelector loads before its content is set (#3316)

MessageSelector.OnLoaded built the TextSelectionManager unconditionally:

_textSelectionManager ??= new TextSelectionManager(this, ContentTemplateRoot);

TextSelectionManager's constructor rejects a null root, so whenever a
container was connected before it had been given content, the constructor
threw ArgumentNullException out of a Loaded handler, which is unhandled and
takes the app down.

Reported by crash telemetry on 12.8.2 ("Value cannot be null. Parameter
name: root"). The reported log tails show it under heavy UI churn — chat
navigation with popups opening — which is when layout is most likely to lag
behind connection.

Attach whenever a content element is actually present instead: on load, and
again from OnContentChanged whenever the content itself changes. The control
is always handed a UIElement directly and is never initialized through a
ContentTemplate, so Content is the element to attach to and ContentTemplateRoot
was the wrong property to read in the first place.

OnContentChanged detaches first. The manager's pointer handlers live on the
previous content element, so they have to come off before attaching to the
new one, or they outlive the element they were added to and the manager keeps
reporting selection for content that is no longer displayed.

EnsureTextSelectionManager also checks IsConnected. Only OnUnloaded and
OnContentChanged detach the manager, so attaching while disconnected would
leave the pointer handlers on the content permanently and, through them, root
the control for the rest of the session.

The four selection accessors dereferenced the manager unconditionally, which
would have turned this window into a NullReferenceException instead; they are
now null-safe.

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

#unigram
🫡2
UnigramDev/Unigram/develop • 6404539 • 1 files, +15/-6
Fix crash opening the context menu on a partially selected message (#3317)

Message_ContextRequested built a MessageQuote straight from the selection and
immediately dereferenced it:

quote = new MessageQuote
{
Quote = parent.GetSelectedSourceText(out selectionStart),
...
};
selectionEnd = selectionStart + quote.Quote.Text.Length;

MessageSelector.HasSelection only reports that a selection is currently shown.
It is a plain flag, tracked separately from the ranges the text actually comes
from, so it can be true while GetSelectedSourceText returns null — when there
are no selected ranges, when the range collapses to nothing in source offsets,
or when the underlying block no longer has source text (GetSourceText returns
null for either of the last two). The result was a NullReferenceException out
of an async void handler, which is unhandled and takes the app down.

Reported by crash telemetry on 12.8.2.

Take the selected text first and only build the quote when there is one.
Everything downstream already treats a null quote as "no quote" — the very
next block reads `if (quote != null && MessageQuote_Loaded(...))` and falls
back to a plain reply — so leaving it unset is the behaviour the caller
already expects.

This only makes the call site agree with what the API can return; the flag and
the ranges are still tracked separately in TextSelectionManager, which is worth
revisiting on its own.

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

#unigram
🫡2
UnigramDev/Unigram/crash-autocomplete-template-mismatch • 381ea2d • 1 files, +24/-14
Fix crash populating autocomplete rows from a mismatched template

Autocomplete_ContainerContentChanging walked the container's visual tree on
the assumption that it matches args.Item:

var content = args.ItemContainer.ContentTemplateRoot as Grid;
var title = content.Children[1] as TextBlock;
var description = title.Inlines[1] as Run;
...
description.Text = ...

The list uses ItemTemplateSelector, so containers are recycled across item
types that have different templates, and ContentTemplateRoot can still be the
previous type's tree when this fires. Every step of that walk is an `as` cast
whose result is dereferenced immediately, so a mismatch is a
NullReferenceException in an event handler — fatal, not caught anywhere.

Reported by crash telemetry on 12.8.2, landing on the QuickReplyShortcut
branch. args.InRecycleQueue was already handled; this is the other half of the
recycling problem, where the container is live but not yet re-templated.

Match the expected shape and bail out when it isn't there, rather than
indexing into whatever tree happens to be present. Applied to the reported
branch and to the User branch, which is structurally identical and has the
same latent fault.

The remaining branches index container children the same way and share the
pattern, but have no reported crashes; leaving them for now to keep the change
reviewable rather than rewriting the whole handler.

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

#unigram
🫡2
UnigramDev/Unigram/crash-sharetarget-null-window • 235fc66 • 1 files, +19/-2
Fix crash activating the app as a share target without a window context

InitializeFrame dereferenced WindowContext.Current unconditionally:

if (WindowContext.Current.Content == null)
{
WindowContext.Current.Content = CreateRootElement(e, WindowContext.Current);
}

WindowContext._current is [ThreadStatic]. It is assigned by the constructor,
which OnWindowCreated calls, and cleared again in OnShutdownCompleted when the
view's dispatcher shuts down. An activation arriving on a view that has been
through that shutdown therefore finds no context, and the app dies with a
NullReferenceException before it has a window.

Its only caller already allows for exactly this:

if (WindowContext.Current?.Content == null)
{
InitializeFrame(e);
}

The null-conditional makes a missing context indistinguishable from missing
content, so the branch is taken and InitializeFrame is called precisely when
there is nothing to dereference.

Build the context instead of assuming one. That is what OnWindowCreated would
have done, and it leaves the frame actually initialized rather than merely not
crashing, so the activation still gets a usable window. Where there is no
Window.Current either, there is nothing to build against; log and return.

Reported by crash telemetry on 12.8.1, all of it through OnShareTargetActivated,
and the code is unchanged on develop.

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

#unigram
🫡2
telegramdesktop/tdesktop/dev • 8e18cb7 • 1 files, +1/-0
Work around bugged D-Bus cmake config

It overrides pkg-config search paths but never restores them leading to pipewire being not found, disable the find_package call so that openal falls back to pkg-config directly

#tdesktop
🫡3
UnigramDev/Unigram/crash-sharetarget-null-window • e30f8e4 • 1 files, +38/-2
Fix crash activating the app as a share target before its window is created

InitializeFrame dereferenced WindowContext.Current unconditionally:

if (WindowContext.Current.Content == null)
{
WindowContext.Current.Content = CreateRootElement(e, WindowContext.Current);
}

Share target activation appears to race window creation: the view has a
Window, but OnWindowCreated has not run yet, so no WindowContext exists and
the app dies with a NullReferenceException before it has any content.

Its only caller already allows for this — it tests WindowContext.Current?.Content
— so the null-conditional makes a missing context indistinguishable from
missing content, and InitializeFrame is called precisely when there is nothing
to dereference.

Make CreateWindowWrapper idempotent, and build the context early when
activation gets there first.

The idempotence is the load-bearing half. WindowContext's constructor assigns
the thread-static Current, so constructing a second one for a view that
already has one replaces it and silently orphans whatever was attached to the
first — including the frame. Creating the context during activation is only
safe because the OnWindowCreated that follows now reuses it instead of
replacing it. It is compared by window rather than assumed, and a context
belonging to a different window is still replaced, as before.

Logging is added on each path — reusing, creating, replacing, activating
early, and activating with no window at all — since this is a race that only
shows up in the field and the log tail is the only way to see which way it
went.

Reported by crash telemetry on 12.8.1, all of it through OnShareTargetActivated,
and the code is unchanged on develop.

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

#unigram
🫡2
UnigramDev/Unigram/develop • 0d19263 • 1 files, +38/-2
Fix crash activating the app as a share target before its window is created (#3320)

InitializeFrame dereferenced WindowContext.Current unconditionally:

if (WindowContext.Current.Content == null)
{
WindowContext.Current.Content = CreateRootElement(e, WindowContext.Current);
}

Share target activation appears to race window creation: the view has a
Window, but OnWindowCreated has not run yet, so no WindowContext exists and
the app dies with a NullReferenceException before it has any content.

Its only caller already allows for this — it tests WindowContext.Current?.Content
— so the null-conditional makes a missing context indistinguishable from
missing content, and InitializeFrame is called precisely when there is nothing
to dereference.

Make CreateWindowWrapper idempotent, and build the context early when
activation gets there first.

The idempotence is the load-bearing half. WindowContext's constructor assigns
the thread-static Current, so constructing a second one for a view that
already has one replaces it and silently orphans whatever was attached to the
first — including the frame. Creating the context during activation is only
safe because the OnWindowCreated that follows now reuses it instead of
replacing it. It is compared by window rather than assumed, and a context
belonging to a different window is still replaced, as before.

Logging is added on each path — reusing, creating, replacing, activating
early, and activating with no window at all — since this is a race that only
shows up in the field and the log tail is the only way to see which way it
went.

Reported by crash telemetry on 12.8.1, all of it through OnShareTargetActivated,
and the code is unchanged on develop.

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

#unigram
🫡2
UnigramDev/Unigram/crash-vlc-dangling-audio-device • 3bd0033 • 1 files, +9/-1
Fix crash when the default audio device changes during playback

OnDefaultAudioRenderDeviceChanged handed libvlc a pointer to a string that
had already been destroyed:

Set(libvlc_audio_output_set, winrt::to_string(args.Id()).c_str());

Set forwards its arguments into a lambda that Write queues for the player's
worker thread, and captures them by value — which for a const char* copies the
pointer, not the characters. The temporary std::string returned by to_string
dies at the end of that statement, so by the time the work item is dequeued
libvlc reads freed memory.

The crash lands in strcmp, called from module_exists by
libvlc_audio_output_set as it compares the device name against known module
names, which is exactly what reading a freed string looks like.

Own the string in the capture instead, matching the Write call sites that
already exist in this file.

This fires whenever the system's default audio render device changes while a
player is alive — headphones plugged in or removed, a Bluetooth device
connecting, a call starting — which is why it is common enough to matter
despite needing a device change to trigger. Reported by crash telemetry on
12.8.1; the code is unchanged on develop.

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

#unigram
🫡3
UnigramDev/Unigram/develop • 6ea41c1 • 1 files, +9/-1
Fix crash when the default audio device changes during playback (#3321)

OnDefaultAudioRenderDeviceChanged handed libvlc a pointer to a string that
had already been destroyed:

Set(libvlc_audio_output_set, winrt::to_string(args.Id()).c_str());

Set forwards its arguments into a lambda that Write queues for the player's
worker thread, and captures them by value — which for a const char* copies the
pointer, not the characters. The temporary std::string returned by to_string
dies at the end of that statement, so by the time the work item is dequeued
libvlc reads freed memory.

The crash lands in strcmp, called from module_exists by
libvlc_audio_output_set as it compares the device name against known module
names, which is exactly what reading a freed string looks like.

Own the string in the capture instead, matching the Write call sites that
already exist in this file.

This fires whenever the system's default audio render device changes while a
player is alive — headphones plugged in or removed, a Bluetooth device
connecting, a call starting — which is why it is common enough to matter
despite needing a device change to trigger. Reported by crash telemetry on
12.8.1; the code is unchanged on develop.

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

#unigram
🫡2
UnigramDev/Unigram/develop • 7bb3a02 • 3 files, +390/-390
Update maximize icon

UnigramDev/Unigram/develop • a4f390f • 4 files, +140/-206
Untangle ImageHelper scaling and crop duplication

The "fit inside N" calculation was spelled out inline seven times. Replace it
with three named primitives — FitRatio, Fit and Clamp — so the difference
between a target and a ceiling is visible at the call site rather than implied
by a parameter named requestedMinSide that holds a maximum.

Drop both Scale overloads. They had no callers, and Scale(this Size, double)
was an exact signature match for SizeEx.Scale in Telegram.Controls, which
multiplies by a factor rather than fitting to a box. Which of the two a file
got depended only on the namespace it sat in.

CropAsync and CropAndPreviewAsync carried a copy each of the same 45 lines,
including the same TODO verbatim. Both now call ComputeCropTransform.

Fix the minimum-size branch, which took the smaller of the two ratios: that
brings the longer edge down to the floor and leaves the shorter one further
below it than it started, so a 100x400 crop with a minimum of 160 came back
40x160. No effect on the only live caller, whose crop is always square.

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

UnigramDev/Unigram/develop • 59b9050 • 3 files, +34/-17
Fix HEVC 10-bit video hanging VideoAnimation

decode_frame gated on a whitelist of pixel formats before the swscale path was
considered, so yuv420p10le was rejected outright and every frame returned -1.
The whitelist describes what libyuv can convert unaided, not what is supported;
it now gates only that fast path, and anything else goes through the scaler.

The alignment test likewise only chooses swscale in preference to libyuv, so it
applies only when a libyuv path exists. Without one the scaler is the only way
to convert the frame, and the scaling path already handles an unaligned
destination through an intermediate buffer.

RenderSync then spun. has_decoded_frames is set as soon as a frame is received,
before conversion, which freezes the try budget; the read-EOF branch does not
reset it. With conversion failing on every frame the loop decoded the file end
to end, seeked back to zero and repeated with no exit condition. A failed
conversion now spends a try, which bounds the loop regardless of the format.

RenderSync returns Boolean rather than Int32. It only ever returned 1 or 0, and
both internal callers already treated the result as a boolean.

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

#unigram
🫡2
UnigramDev/Unigram/develop • 27022ab • 1 files, +25/-6
Propagate video render failures out of ImageHelper

RenderSync answers whether it wrote a frame, and three call sites discarded it,
passing a BufferSurface that still held uninitialised memory to Encode or to a
WriteableBitmap.

Each needs a different answer. GetPreviewBitmapAsync returns null, which is
already its contract and which every caller substitutes a placeholder for.
CropAndPreviewAsync throws, because StorageMedia.Refresh recovers by falling
back to the unedited preview and does so from a catch, which a null would slip
past. OpenReadAsync throws, because callers pass the result straight to
BitmapDecoder and there is no null that does not simply move the failure; both
GenerationService and ImageCropper.SetSourceAsync catch it.

The blanket catch in GetPreviewBitmapAsync now logs. A codec the decoder cannot
handle was indistinguishable from a file with no frames.

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

UnigramDev/Unigram/develop • d4ac632 • 4 files, +208/-86
Remove per-frame allocations from message layout

Four allocations on paths that run for every message on every layout pass.

ReactionsPanel.ArrangeOverride built a List of rows and a List of rects per row
purely to centre them. A row is a contiguous run of children, complete as soon
as the next child does not fit, at which point its right edge is known, so each
run is now arranged in place. Both overrides also index Children rather than
enumerating it, since foreach over UIElementCollection allocates an
IEnumerator<UIElement> per pass.

LoadingTextBlock rebuilt its skeleton clip on every arrange: a native text
measurement, one D2D geometry per line, a group, a path, a path geometry and a
clip. It is now cached on the inputs it derives from.

ReplyMarkupInlinePanel rebuilt the rect buffers it hands to the native clip
builder, plus the clip itself, on every arrange. The buffers are reused and the
rects compared in place, so the clip is rebuilt only when one moves.

Discern gains an IList<UnreadReaction> overload over a shared core, so
ReactionsPanel no longer projects through Select on a list that is usually
empty.

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

UnigramDev/Unigram/develop • a2c8668 • 5 files, +28/-7
Release chart geometries built inside the draw pass

DrawChart and DrawPickerChart called CanvasGeometry.CreatePath once per line on
every frame and never disposed the result, leaving the native D2D geometry to
the finalizer. Seven sites now go through DrawGeometry and FillGeometry on
BaseChartView, which wrap the geometry in a using. The drawing session consumes
it before the call returns, so nothing outlives the scope.

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

#unigram
🫡2
UnigramDev/Unigram/develop • ba4ca6b • 1 files, +54/-18
Fix resource ownership and leaks in DrawStrokes

DrawStrokes is public, took a CanvasBitmap as a parameter and disposed it, so
any caller outside this file lost the bitmap it passed in. The caller now owns
both the bitmap it supplies and the render target it receives, and the summary
says so.

Nothing was scoped, so an exception between the two CanvasRenderTarget
constructions and the final dispose leaked all three objects. The body moves to
a private overload, leaving the public one to own the targets: the overlay is a
using and the returned target is disposed on the way out of a catch.

DrawStrokesAsync(SoftwareBitmap) leaked the InMemoryRandomAccessStream it
returns when SaveAsync threw. DrawStrokesAsync(StorageFile) relied on
DrawStrokes disposing the bitmap before the file was reopened for writing,
since it is loaded from that same path; the release is now explicit rather than
a side effect, and scoped to keep that order.

canvas1 and canvas2 become overlay and target.

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

UnigramDev/Unigram/develop • 532b8cf • 1 files, +20/-12
Drop dead parameters and stale docs from ImageHelper

CropAsync took a quality parameter that nothing read: the property set it was
written for is commented out. No caller passed it, and none passes more than
five positional arguments, so removing it does not disturb them.

CropAsync also took trimStart and never forwarded it to OpenReadAsync, so a
trimmed video would have been cropped at position zero. ScaleAsync already
forwards its own; this one now does too. No caller passes it yet, so nothing
changes today.

The doc block above ScaleAsync described a quality parameter the method has
never had, and omitted the ones it does. Both commented-out property sets
referred to that parameter by name.

RotateArea computed width - (width - area.X), which is area.X, through an
intermediate Point.

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

UnigramDev/Unigram/develop • bfb6d0d • 1 files, +3/-1
Accept every video container in OpenReadAsync

The frame extraction path tested for the literal ".mp4", so .mov and .m4v took
the image branch and failed inside BitmapDecoder. Constants.VideoTypes is the
set EditMediaPopup already uses to decide whether a file is a video, and the two
disagreed.

Uses the existing HasExtension helper, which compares ordinal and
case-insensitive against the array without copying it.

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

#unigram
🫡2