UnigramDev/Unigram/dwrite-collection-key • c1bf81a • 2 files, +40/-11
Give the DWrite custom font collection a valid key
CreateCustomFontCollection takes the key size in bytes, but it was given
path1.size() + path2.size(), the summed character length of the two package
paths (~240) — while the key itself is a two-element array of pointers, 16
bytes. DWrite copied ~240 bytes out of a 16-byte stack array on every
PlaceholderImageHelper construction, so the key it stored was partly unrelated
stack memory, and how much varied with the install path length.
The key also pointed at two locals that die when the function returns, so the
pointers DWrite kept were dangling by the time it could hand them back to
CreateEnumeratorFromKey. The paths and the pointer array now live inside the
CustomFontLoader, which stays alive as long as it is registered, and the size
passed is sizeof that array. CustomFontFileEnumerator honours collectionKeySize
instead of assuming two entries.
Close() now unregisters the loader it registered on the shared DWrite factory,
after releasing the collection built from it. The factory is process-wide, so
without this every window thread left a loader and a custom collection on it for
the rest of the session.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Give the DWrite custom font collection a valid key
CreateCustomFontCollection takes the key size in bytes, but it was given
path1.size() + path2.size(), the summed character length of the two package
paths (~240) — while the key itself is a two-element array of pointers, 16
bytes. DWrite copied ~240 bytes out of a 16-byte stack array on every
PlaceholderImageHelper construction, so the key it stored was partly unrelated
stack memory, and how much varied with the install path length.
The key also pointed at two locals that die when the function returns, so the
pointers DWrite kept were dangling by the time it could hand them back to
CreateEnumeratorFromKey. The paths and the pointer array now live inside the
CustomFontLoader, which stays alive as long as it is registered, and the size
passed is sizeof that array. CustomFontFileEnumerator honours collectionKeySize
instead of assuming two entries.
Close() now unregisters the loader it registered on the shared DWrite factory,
after releasing the collection built from it. The factory is process-wide, so
without this every window thread left a loader and a custom collection on it for
the rest of the session.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/gift-variants-win10 • d730aa9 • 2 files, +6/-2
Fix InvalidCastException opening the gift variants popup on Windows 10
ListViewItemPresenter.SelectedBorderBrush, SelectedPointerOverBorderBrush and
SelectedPressedBorderBrush are declared on IListViewItemPresenter4, introduced
in UniversalApiContract 13.0 (build 22621). On older builds the QueryInterface
behind the setter fails and the projection throws, so GiftVariantsPopup crashed
as soon as the first container was realized.
Guard the three assignments on the property actually being present. Where it is
not, the selection border keeps its default brush.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix InvalidCastException opening the gift variants popup on Windows 10
ListViewItemPresenter.SelectedBorderBrush, SelectedPointerOverBorderBrush and
SelectedPressedBorderBrush are declared on IListViewItemPresenter4, introduced
in UniversalApiContract 13.0 (build 22621). On older builds the QueryInterface
behind the setter fails and the projection throws, so GiftVariantsPopup crashed
as soon as the first container was realized.
Guard the three assignments on the property actually being present. Where it is
not, the selection border keeps its default brush.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/invite-links-full-info • 5e526bd • 3 files, +18/-2
Let the invite link actually arrive on ChatInviteLinksPage
ChatInviteLinksViewModel declares _supergroupId and _basicGroupId but never
assigns them, so Handle(UpdateSupergroupFullInfo) and
Handle(UpdateBasicGroupFullInfo) compared against 0 and never matched. The
"full info will arrive by push" fallback in OnNavigatedToAsync was therefore
dead, and InviteLink stayed null for every chat whose full info wasn't already
cached — which made the header's copy and share buttons dereference null.
Assign both ids, and fix the basic group branch, which asked for
GetBasicGroupFullInfo(supergroup.Id) inside the branch where TryGetSupergroup
had just failed: supergroup is null there, and it was passing a supergroup id
where a basic group id belongs.
InviteLink is also legitimately null for anyone who isn't the creator, since
TDLib only populates the primary link for them, so the header buttons are now
disabled while it is null instead of acting on nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Let the invite link actually arrive on ChatInviteLinksPage
ChatInviteLinksViewModel declares _supergroupId and _basicGroupId but never
assigns them, so Handle(UpdateSupergroupFullInfo) and
Handle(UpdateBasicGroupFullInfo) compared against 0 and never matched. The
"full info will arrive by push" fallback in OnNavigatedToAsync was therefore
dead, and InviteLink stayed null for every chat whose full info wasn't already
cached — which made the header's copy and share buttons dereference null.
Assign both ids, and fix the basic group branch, which asked for
GetBasicGroupFullInfo(supergroup.Id) inside the branch where TryGetSupergroup
had just failed: supergroup is null there, and it was passing a supergroup id
where a basic group id belongs.
InviteLink is also legitimately null for anyone who isn't the creator, since
TDLib only populates the primary link for them, so the header buttons are now
disabled while it is null instead of acting on nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/location-access-guard • ec3172b • 1 files, +12/-1
Guard the geolocation access request in SendLocationPopup
Geolocator.RequestAccessAsync is a remote procedure call and throws when the
Windows Geolocation Service (lfsvc) is disabled, which privacy/debloat scripts
routinely do. In FindLocation the call was the only one not wrapped, so the
throw escaped an async void method and reached the unhandled exception handler.
Treat a failed request as "not allowed": the popup keeps the map shimmer, the
same as an explicit denial.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Guard the geolocation access request in SendLocationPopup
Geolocator.RequestAccessAsync is a remote procedure call and throws when the
Windows Geolocation Service (lfsvc) is disabled, which privacy/debloat scripts
routinely do. In FindLocation the call was the only one not wrapped, so the
throw escaped an async void method and reached the unhandled exception handler.
Treat a failed request as "not allowed": the popup keeps the map shimmer, the
same as an explicit denial.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/ocr-extract-recovery • c3efb81 • 1 files, +64/-20
Recover from a damaged OCR model archive
Extracting Ocr_x64.zip crashed the app with an InvalidDataException,
"The archive entry was compressed using an unsupported compression
method". The message is misleading: Inflater maps zlib's Z_DATA_ERROR
onto that string, and the failure came from mid-entry writes in
DeflateStream.CopyToAsyncStream rather than from ZipArchiveEntry.Open,
so the deflate data was corrupt or truncated, not compressed with a
method the reader doesn't support. Why the local copy was damaged is
not established.
ExtractModelAsync had no try/catch and RecognizeText is async void, so
the failure reached the app unhandled. Catch it, delete the archive so
that the next attempt downloads a fresh copy, delete the partially
written model so that the readiness check in EnsureReadyAsync doesn't
accept it, and report the extraction as unavailable.
Separately, _extractLock.Release() sat outside any finally, so a throw
during extraction leaked the semaphore for the rest of the session:
every later extraction then returned early at Wait(0), leaving OCR
broken until restart.
Reported by crash telemetry on 12.9.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Recover from a damaged OCR model archive
Extracting Ocr_x64.zip crashed the app with an InvalidDataException,
"The archive entry was compressed using an unsupported compression
method". The message is misleading: Inflater maps zlib's Z_DATA_ERROR
onto that string, and the failure came from mid-entry writes in
DeflateStream.CopyToAsyncStream rather than from ZipArchiveEntry.Open,
so the deflate data was corrupt or truncated, not compressed with a
method the reader doesn't support. Why the local copy was damaged is
not established.
ExtractModelAsync had no try/catch and RecognizeText is async void, so
the failure reached the app unhandled. Catch it, delete the archive so
that the next attempt downloads a fresh copy, delete the partially
written model so that the readiness check in EnsureReadyAsync doesn't
accept it, and report the extraction as unavailable.
Separately, _extractLock.Release() sat outside any finally, so a throw
during extraction leaked the semaphore for the rest of the session:
every later extraction then returned early at Wait(0), leaving OCR
broken until restart.
Reported by crash telemetry on 12.9.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/profile-topics-context-menu • e3b452d • 1 files, +19/-8
Fix the Topics tab context menu casting to the wrong item type
ProfileTopicsTabPage.OnContextRequested was an unadapted copy of the one in
ProfileSavedChatsTabPage: it cast the right-clicked item to SavedMessagesTopic,
which is always null here because this list holds ForumTopic, and it invoked
the saved chats tab's commands instead of the topics tab's own. Right-clicking
any row threw a NullReferenceException, reported by crash telemetry on 12.9.1.
Cast to ForumTopic, route pin/delete to TopicsTab, and gate them on
CanManageTopics the way ForumView's own topic menu does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Fix the Topics tab context menu casting to the wrong item type
ProfileTopicsTabPage.OnContextRequested was an unadapted copy of the one in
ProfileSavedChatsTabPage: it cast the right-clicked item to SavedMessagesTopic,
which is always null here because this list holds ForumTopic, and it invoked
the saved chats tab's commands instead of the topics tab's own. Right-clicking
any row threw a NullReferenceException, reported by crash telemetry on 12.9.1.
Cast to ForumTopic, route pin/delete to TopicsTab, and gate them on
CanManageTopics the way ForumView's own topic menu does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/voip-video-device-lost • 8a6a679 • 1 files, +5/-1
Discard the video resources when the rendering device is replaced
VoipVideoOutput's RenderingDeviceReplaced handler only flipped m_resourcesValid
back on, keeping the YUV420 effect and its three input bitmaps, all of which were
realized on the device that had just been replaced. The next frame of the same
size copied into those bitmaps and drew the effect on a context BeginDraw returned
from the new device, which fails with D2DERR_WRONG_RESOURCE_DOMAIN, "The resource
was realized on the wrong render target".
Call the existing ReleaseShader() from the handler, under the m_deviceMutex it
already holds, so the next frame recreates everything against the new device. The
sibling surfaces already do this: FreeformGradientSurface nulls its bitmap and
re-invalidates, and MessageBubbleNineGrid caches no device-bound resources.
Also fix a self-comparison in RenderFrame: finalSize.cy was compared with itself
instead of m_surfaceSize.cy, so a height-only change never resized the drawing
surface.
Reported by crash telemetry on 12.9.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Discard the video resources when the rendering device is replaced
VoipVideoOutput's RenderingDeviceReplaced handler only flipped m_resourcesValid
back on, keeping the YUV420 effect and its three input bitmaps, all of which were
realized on the device that had just been replaced. The next frame of the same
size copied into those bitmaps and drew the effect on a context BeginDraw returned
from the new device, which fails with D2DERR_WRONG_RESOURCE_DOMAIN, "The resource
was realized on the wrong render target".
Call the existing ReleaseShader() from the handler, under the m_deviceMutex it
already holds, so the next frame recreates everything against the new device. The
sibling surfaces already do this: FreeformGradientSurface nulls its bitmap and
re-invalidates, and MessageBubbleNineGrid caches no device-bound resources.
Also fix a self-comparison in RenderFrame: finalSize.cy was compared with itself
instead of m_surfaceSize.cy, so a height-only change never resized the drawing
surface.
Reported by crash telemetry on 12.9.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/pending-message-reinsert • 003181f • 1 files, +13/-5
Recompute the message index after removing it from the collection
InsertMessageInOrder moves a message by removing it and reinserting it at the
index NextIndexOf computed against the collection as it was *before* the
removal, compensating for the single row that goes away. MessageCollection
removes more than one row: RemoveItem also drops the date or topic separator
that the removal orphans, so a message that was the only one of its day takes
its header with it and the collection shrinks by two. If the message is not the
last row -- a sponsored message below it, say -- the adjusted index is then past
the end and Insert throws ArgumentOutOfRangeException.
Reported by crash telemetry on 12.9.1, reached when a locally pending message
completes: PendingMessage_Completed -> InsertMessage -> InsertMessageInOrder.
Ask NextIndexOf again after the removal rather than trusting the pre-removal
index; clamping would have put the message in the wrong place instead. The
force branch had the same defect -- it reinserted at oldIndex, which is out of
bounds once the header ahead of the message is gone -- so both paths now share
one helper.
Not built: the file parses with Roslyn, nothing more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Recompute the message index after removing it from the collection
InsertMessageInOrder moves a message by removing it and reinserting it at the
index NextIndexOf computed against the collection as it was *before* the
removal, compensating for the single row that goes away. MessageCollection
removes more than one row: RemoveItem also drops the date or topic separator
that the removal orphans, so a message that was the only one of its day takes
its header with it and the collection shrinks by two. If the message is not the
last row -- a sponsored message below it, say -- the adjusted index is then past
the end and Insert throws ArgumentOutOfRangeException.
Reported by crash telemetry on 12.9.1, reached when a locally pending message
completes: PendingMessage_Completed -> InsertMessage -> InsertMessageInOrder.
Ask NextIndexOf again after the removal rather than trusting the pre-removal
index; clamping would have put the message in the wrong place instead. The
force branch had the same defect -- it reinserted at oldIndex, which is out of
bounds once the header ahead of the message is gone -- so both paths now share
one helper.
Not built: the file parses with Roslyn, nothing more.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/chat-preview-teardown • 9b344a2 • 4 files, +38/-7
Tear down the chat preview from the flyout's Closed event
ChatCell.ShowPreview hosts a second ChatView inside a MenuFlyout and hung its only
teardown on chatView.Unloaded, which never fires for a flyout-hosted view: the log
tail of the crash runs for another 109 seconds without the handler's "Unloaded"
line ever appearing. ChatView.OnCollectionChanged therefore stays subscribed to the
view model's Items after the flyout's XAML peers are gone, and the next collection
change reads Messages.ItemsPanelRoot through a separated RCW, throwing
InvalidComObjectException.
Teardown now hangs off the flyout's own Closed event, with Unloaded kept as a
secondary trigger, and ChatView.Deactivate ignores a second call so the order of
the two doesn't matter. The two forum topic cells already tore down from
flyout.Closing and so never had the hole; they move to Closed for a single pattern.
Reported by crash telemetry on 12.9.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Tear down the chat preview from the flyout's Closed event
ChatCell.ShowPreview hosts a second ChatView inside a MenuFlyout and hung its only
teardown on chatView.Unloaded, which never fires for a flyout-hosted view: the log
tail of the crash runs for another 109 seconds without the handler's "Unloaded"
line ever appearing. ChatView.OnCollectionChanged therefore stays subscribed to the
view model's Items after the flyout's XAML peers are gone, and the next collection
change reads Messages.ItemsPanelRoot through a separated RCW, throwing
InvalidComObjectException.
Teardown now hangs off the flyout's own Closed event, with Unloaded kept as a
secondary trigger, and ChatView.Deactivate ignores a second call so the order of
the two doesn't matter. The two forum topic cells already tore down from
flyout.Closing and so never had the hole; they move to Closed for a single pattern.
Reported by crash telemetry on 12.9.1.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • 5623245 • 1 files, +34/-5
Trim the namespace and class from native log lines
MSVC expands __FUNCTION__ to the fully qualified name, so every line logged from
Telegram.Native carried the same winrt::...::implementation:: prefix, followed by
a class the file name already gives:
[1786432732.490][AsyncMediaPlayer.cpp:376][winrt::Telegram::Native::Media::implementation::AsyncMediaPlayer::Close]
Keep only what follows the class, which is also what [CallerMemberName] passes in
from the managed side. Cutting at the last :: instead would have reduced a lambda
to a bare "operator ()", so the enclosing method is kept there.
The std::string temporary goes with it: to_hstring takes anything convertible to
string_view, and these names are well past the small string limit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 1b3d6a9 • 4 files, +683/-1
Copy/paste in Telegram Android's rich HTML format
ProseMirror serialized its own render DOM to the clipboard (div.pm-pullquote,
span.pm-spoiler and friends), which Android's RichHtml.parse flattens to loose
paragraphs: every quote, list and table lost its structure in both directions.
Adds a clipboardSerializer/clipboardParser pair speaking the dialect Android
writes and reads, so a selection round-trips between the two clients. Three
deliberate deviations, all of which its parser accepts:
- checklist state is emitted (it reads data-checkbox/data-checked but never
writes them), as bare attributes since it tests for presence;
- a nested list is written inside the <li> that owns it, which is what its
parser expects — it writes them as a sibling of the <li> and then drops them
on the way back in;
- nodes it has no tag for (math, buttons, anchors, mentions) ride in data-*
attributes on a tag it degrades sensibly.
Media follows the same trade as its RichMediaClipboard: the HTML carries only
the file id, the attrs behind it live in an in-process registry until the next
copy, and an id we can't resolve is dropped rather than pasted broken.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Trim the namespace and class from native log lines
MSVC expands __FUNCTION__ to the fully qualified name, so every line logged from
Telegram.Native carried the same winrt::...::implementation:: prefix, followed by
a class the file name already gives:
[1786432732.490][AsyncMediaPlayer.cpp:376][winrt::Telegram::Native::Media::implementation::AsyncMediaPlayer::Close]
Keep only what follows the class, which is also what [CallerMemberName] passes in
from the managed side. Cutting at the last :: instead would have reduced a lambda
to a bare "operator ()", so the enclosing method is kept there.
The std::string temporary goes with it: to_hstring takes anything convertible to
string_view, and these names are well past the small string limit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 1b3d6a9 • 4 files, +683/-1
Copy/paste in Telegram Android's rich HTML format
ProseMirror serialized its own render DOM to the clipboard (div.pm-pullquote,
span.pm-spoiler and friends), which Android's RichHtml.parse flattens to loose
paragraphs: every quote, list and table lost its structure in both directions.
Adds a clipboardSerializer/clipboardParser pair speaking the dialect Android
writes and reads, so a selection round-trips between the two clients. Three
deliberate deviations, all of which its parser accepts:
- checklist state is emitted (it reads data-checkbox/data-checked but never
writes them), as bare attributes since it tests for presence;
- a nested list is written inside the <li> that owns it, which is what its
parser expects — it writes them as a sibling of the <li> and then drops them
on the way back in;
- nodes it has no tag for (math, buttons, anchors, mentions) ride in data-*
attributes on a tag it degrades sensibly.
Media follows the same trade as its RichMediaClipboard: the HTML carries only
the file id, the attrs behind it live in an in-process registry until the next
copy, and an id we can't resolve is dropped rather than pasted broken.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • dbdb4b7 • 6 files, +271/-230
Give SendFilesPopup rows of its own
StorageAlbum was answering two questions at once. It means one outgoing
message, which is what GetItemsView produces, but the template selector read it
as "a row that draws a mosaic" -- and those disagree in both directions, so
UpdateCollection had to lie three times to bridge them.
A standalone muted video was wrapped in a one-item album with a negative
ordinal, to keep a thumbnail it would otherwise lose. A documents album was
expanded and its grouping thrown away. And a files-mode row had its item
wrapped in a StorageDocument purely to carry the glyph, which
ItemsView_CollectionChanged then unwrapped again to get back to the item the
popup actually holds.
The list now holds a StorageRow: a MosaicRow of media to draw as a mosaic, or a
FileRow of one item with the flag that used to be a wrapper object. The
selector asks the question it means. StorageAlbum goes back to being purely a
send grouping -- Ordinal, Update, the mosaic layout and the display constants
all moved to MosaicRow, and StorageAlbumPanel became MosaicPanel so the naming
stops contradicting itself.
That removes a bug rather than only tidying. OnContainerContentChanging set the
delete button's Tag to the displayed item and Remove_Click looks that up in
Items, but in files mode the displayed item was a wrapper that was never in
Items, so the removal found nothing: deleting a photo or video row while
sending as files did nothing at all. FileRow.Media is the item itself.
Two things kept as they were. Files mode still names the file rather than the
track, which needed an explicit guard once the wrapper stopped hiding the audio
type. And mosaic identity counts mosaic rows rather than list positions, so a
file row appearing between two of them does not renumber everything after it.
StorageDocument's wrapping constructor and its Original property go too, the
popup having been their only consumer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • eb349af • 7 files, +1556/-22
Paste rich HTML into the chat field as blocks
Pasting into the chat field took plain text and nothing else, so copying out of
the rich editor lost every quote, list and table on the way back in.
Adds RichHtml.Parse: the clipboard HTML the editor writes — and the dialect
Telegram Android writes — read back into page blocks. Unlike Android, whose
editor holds a flat list of rows, this builds the nested pageBlock* tree TDLib
expects, and closes the tags HTML closes for you (an unclosed <p> otherwise
swallows the rest as one paragraph, and real clipboard HTML is full of them).
A paste becomes message text whenever TryGetFormattedText can say the whole
thing with entities. When it can't, only the app's own content — stamped with
data-telegram-rich on copy — reopens the rich editor, on the field's text split
around the pasted blocks: it's a separate window and a paid feature, so a
heading copied from a web page falls back to a plain paste instead.
Media is dropped. The HTML carries only a file id, which means nothing outside
the process that copied it, so a photo can't be rebuilt here — Android does the
same with an id it can't resolve, and an orphaned caption stays as a paragraph.
What markup can't express — a button's style and type — travels as the TDLib
JSON ClientJson reads back.
SendRichMessage now opens the editor on an existing rich draft rather than on
the text field, and PasteRichMessage splices into the same source of truth.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Give SendFilesPopup rows of its own
StorageAlbum was answering two questions at once. It means one outgoing
message, which is what GetItemsView produces, but the template selector read it
as "a row that draws a mosaic" -- and those disagree in both directions, so
UpdateCollection had to lie three times to bridge them.
A standalone muted video was wrapped in a one-item album with a negative
ordinal, to keep a thumbnail it would otherwise lose. A documents album was
expanded and its grouping thrown away. And a files-mode row had its item
wrapped in a StorageDocument purely to carry the glyph, which
ItemsView_CollectionChanged then unwrapped again to get back to the item the
popup actually holds.
The list now holds a StorageRow: a MosaicRow of media to draw as a mosaic, or a
FileRow of one item with the flag that used to be a wrapper object. The
selector asks the question it means. StorageAlbum goes back to being purely a
send grouping -- Ordinal, Update, the mosaic layout and the display constants
all moved to MosaicRow, and StorageAlbumPanel became MosaicPanel so the naming
stops contradicting itself.
That removes a bug rather than only tidying. OnContainerContentChanging set the
delete button's Tag to the displayed item and Remove_Click looks that up in
Items, but in files mode the displayed item was a wrapper that was never in
Items, so the removal found nothing: deleting a photo or video row while
sending as files did nothing at all. FileRow.Media is the item itself.
Two things kept as they were. Files mode still names the file rather than the
track, which needed an explicit guard once the wrapper stopped hiding the audio
type. And mosaic identity counts mosaic rows rather than list positions, so a
file row appearing between two of them does not renumber everything after it.
StorageDocument's wrapping constructor and its Original property go too, the
popup having been their only consumer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • eb349af • 7 files, +1556/-22
Paste rich HTML into the chat field as blocks
Pasting into the chat field took plain text and nothing else, so copying out of
the rich editor lost every quote, list and table on the way back in.
Adds RichHtml.Parse: the clipboard HTML the editor writes — and the dialect
Telegram Android writes — read back into page blocks. Unlike Android, whose
editor holds a flat list of rows, this builds the nested pageBlock* tree TDLib
expects, and closes the tags HTML closes for you (an unclosed <p> otherwise
swallows the rest as one paragraph, and real clipboard HTML is full of them).
A paste becomes message text whenever TryGetFormattedText can say the whole
thing with entities. When it can't, only the app's own content — stamped with
data-telegram-rich on copy — reopens the rich editor, on the field's text split
around the pasted blocks: it's a separate window and a paid feature, so a
heading copied from a web page falls back to a plain paste instead.
Media is dropped. The HTML carries only a file id, which means nothing outside
the process that copied it, so a photo can't be rebuilt here — Android does the
same with an id it can't resolve, and an orphaned caption stays as a paragraph.
What markup can't express — a button's style and type — travels as the TDLib
JSON ClientJson reads back.
SendRichMessage now opens the editor on an existing rich draft rather than on
the text field, and PasteRichMessage splices into the same source of truth.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • 1208597 • 2 files, +57/-1
Focus the editor when its window opens
The caret only ever appeared after re-activating the editor window. Two reasons,
both of them ordering:
TryFocus focused the document before the control. The JS view.focus() sets
document.activeElement inside a browser whose host has no focus — ProseMirror
even draws its selection — but the keyboard stays with XAML. Focusing the
control is what routes input into the web content; the document call only
places the caret, so it has to come second.
And ViewService.OpenAsync activates the window as soon as its content exists,
which is long before CoreWebView2 does. XAML's initial focus lands on the
WebView2 while it has no controller to forward it to, so that GotFocus goes
nowhere — and focusing it later is a no-op, because the control already has it.
Re-activating the window worked precisely because it raised GotFocus again.
Focus is now taken on activation (deferred, since XAML assigns its own while
activating) and, when the control is the one already holding it, moved off and
back so the transition actually happens.
Also drops the WebView2's Source: Initialize navigates to the same file through
the editor.unigram virtual host, so setting both loaded the whole editor twice
— two parses of the bundle and two ProseMirror mounts per open.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 2302a74 • 1 files, +2/-2
Hook SizeChanged to the right container
UnigramDev/Unigram/develop • 5e572a3 • 2 files, +9/-7
Fix purchase button
UnigramDev/Unigram/develop • 34ecd82 • 2 files, +8/-0
Fix default editor state
#unigram
Focus the editor when its window opens
The caret only ever appeared after re-activating the editor window. Two reasons,
both of them ordering:
TryFocus focused the document before the control. The JS view.focus() sets
document.activeElement inside a browser whose host has no focus — ProseMirror
even draws its selection — but the keyboard stays with XAML. Focusing the
control is what routes input into the web content; the document call only
places the caret, so it has to come second.
And ViewService.OpenAsync activates the window as soon as its content exists,
which is long before CoreWebView2 does. XAML's initial focus lands on the
WebView2 while it has no controller to forward it to, so that GotFocus goes
nowhere — and focusing it later is a no-op, because the control already has it.
Re-activating the window worked precisely because it raised GotFocus again.
Focus is now taken on activation (deferred, since XAML assigns its own while
activating) and, when the control is the one already holding it, moved off and
back so the transition actually happens.
Also drops the WebView2's Source: Initialize navigates to the same file through
the editor.unigram virtual host, so setting both loaded the whole editor twice
— two parses of the bundle and two ProseMirror mounts per open.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UnigramDev/Unigram/develop • 2302a74 • 1 files, +2/-2
Hook SizeChanged to the right container
UnigramDev/Unigram/develop • 5e572a3 • 2 files, +9/-7
Fix purchase button
UnigramDev/Unigram/develop • 34ecd82 • 2 files, +8/-0
Fix default editor state
#unigram
UnigramDev/Unigram/develop • 50ca959 • 32 files, +2975/-60
Pin vcpkg with a manifest instead of a hand-prepared checkout
Setting up the unmanaged dependencies meant cloning vcpkg at a 2024 commit,
editing the ffmpeg portfile by hand to insert an 85-flag `--enable-*` string,
applying Libraries/vcpkg.patch to vcpkg's own MSBuild targets, running
`vcpkg integrate install`, and installing fourteen packages per triplet from
the command line. None of it was verifiable, and that baseline can no longer
build from scratch: the msys2 runtime package it pins has fallen off every
mirror, so vcpkg_fixup_pkgconfig fails with a 404.
vcpkg.json pins 2026.03.18 and declares what is actually used. That is nine
ports, not the fourteen that were installed: cppwinrt comes from the NuGet
package and flatbuffers from the submodule, while dav1d, libvpx and
libjpeg-turbo resolve transitively. The commit matches the one TDLib
documents, so openssl and zlib cannot drift between the tdjson.dll we ship
and the copies the app links -- they reach the flat package root under the
same names from both.
ffmpeg needs a modified portfile for its decoder list, so it is vendored as
an overlay port, taken from the registry at 7.1.2 with the flag line applied.
It wins over the baseline's 8.0.1, which keeps the sonames at avcodec-61 and
avoids ffmpeg 8 removing avcodec_close out from under tgcalls. Moving to 8 is
now a self-contained change.
Directory.Build.props finds vcpkg without any environment variable -- a
checkout beside the repository, or the copy that ships with Visual Studio --
and turns off the machine-wide integration so the pin cannot be bypassed. A
guard reports a checkout older than the pin, which otherwise fails as "no
version database entry", since vcpkg reads the version database from the
working tree rather than from the pinned commit.
The runtime DLLs are copied through ReferenceCopyLocalPaths rather than
vcpkg's applocal step, which made Libraries/vcpkg.patch necessary in the first
place. applocal does not run when the linker is skipped or when a project is
only queried for its packaging outputs, and the DLLs then go missing with no
error; the patch covered one of those cases and never the other. Declaring
them also puts them in the app's own output folder, which applocal never did.
openssl and zlib are excluded there because Libraries/tdjson ships the copies
tdjson.dll was linked against, under the same file names.
Libraries/tdjson/build.ps1 builds TDLib from the same manifest and the same
installed tree, so there is one openssl and one zlib in the build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Pin vcpkg with a manifest instead of a hand-prepared checkout
Setting up the unmanaged dependencies meant cloning vcpkg at a 2024 commit,
editing the ffmpeg portfile by hand to insert an 85-flag `--enable-*` string,
applying Libraries/vcpkg.patch to vcpkg's own MSBuild targets, running
`vcpkg integrate install`, and installing fourteen packages per triplet from
the command line. None of it was verifiable, and that baseline can no longer
build from scratch: the msys2 runtime package it pins has fallen off every
mirror, so vcpkg_fixup_pkgconfig fails with a 404.
vcpkg.json pins 2026.03.18 and declares what is actually used. That is nine
ports, not the fourteen that were installed: cppwinrt comes from the NuGet
package and flatbuffers from the submodule, while dav1d, libvpx and
libjpeg-turbo resolve transitively. The commit matches the one TDLib
documents, so openssl and zlib cannot drift between the tdjson.dll we ship
and the copies the app links -- they reach the flat package root under the
same names from both.
ffmpeg needs a modified portfile for its decoder list, so it is vendored as
an overlay port, taken from the registry at 7.1.2 with the flag line applied.
It wins over the baseline's 8.0.1, which keeps the sonames at avcodec-61 and
avoids ffmpeg 8 removing avcodec_close out from under tgcalls. Moving to 8 is
now a self-contained change.
Directory.Build.props finds vcpkg without any environment variable -- a
checkout beside the repository, or the copy that ships with Visual Studio --
and turns off the machine-wide integration so the pin cannot be bypassed. A
guard reports a checkout older than the pin, which otherwise fails as "no
version database entry", since vcpkg reads the version database from the
working tree rather than from the pinned commit.
The runtime DLLs are copied through ReferenceCopyLocalPaths rather than
vcpkg's applocal step, which made Libraries/vcpkg.patch necessary in the first
place. applocal does not run when the linker is skipped or when a project is
only queried for its packaging outputs, and the DLLs then go missing with no
error; the patch covered one of those cases and never the other. Declaring
them also puts them in the app's own output folder, which applocal never did.
openssl and zlib are excluded there because Libraries/tdjson ships the copies
tdjson.dll was linked against, under the same file names.
Libraries/tdjson/build.ps1 builds TDLib from the same manifest and the same
installed tree, so there is one openssl and one zlib in the build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/develop • a0f0499 • 36 files, +3941/-0
Add a benchmark harness for the TDLib JSON path
Measures what Client.Receive and ClientJson.FromJson actually cost per payload,
on all three toolchains in play: the desktop JIT under BenchmarkDotNet, UWP on
.NET 10 with NativeAOT, and UWP on .NET Native, which is what ships. One Suite.cs
runs on all three so their numbers line up row for row.
Nothing here touches the app. The projects reference Telegram.Generators and
Libraries/tdjson/td_api.tl and link ClientJson.cs, so the code under test is the
generated parser rather than a copy that would drift from it. Round trips go
through the real tdjson.dll using TDLib's own offline test methods, which need no
account and no network.
Findings are written up in Telegram.Benchmarks/README.md. The load-bearing one:
UWP resolves System.Memory's netstandard2.0 asset, so Span<T> is not the runtime's
own and indexing one costs ~10x what the same access over byte[] or byte* costs.
That accounts for the parse being ~3x slower on .NET Native than the desktop
numbers suggested, and it is why Json/TdJsonReader.cs walks raw memory instead:
3.1-3.7x faster than Utf8JsonReader there, with the same allocations.
That reader is a prototype and is not wired into the generator yet. It is
bounds-checked against truncated input, proven by a guard-page sweep over every
prefix of every corpus payload - 70,512 of them - and Validation.cs runs on all
three hosts, so agreement with Utf8JsonReader is checked against the
netstandard2.0 System.Text.Json as well as the net10.0 one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Add a benchmark harness for the TDLib JSON path
Measures what Client.Receive and ClientJson.FromJson actually cost per payload,
on all three toolchains in play: the desktop JIT under BenchmarkDotNet, UWP on
.NET 10 with NativeAOT, and UWP on .NET Native, which is what ships. One Suite.cs
runs on all three so their numbers line up row for row.
Nothing here touches the app. The projects reference Telegram.Generators and
Libraries/tdjson/td_api.tl and link ClientJson.cs, so the code under test is the
generated parser rather than a copy that would drift from it. Round trips go
through the real tdjson.dll using TDLib's own offline test methods, which need no
account and no network.
Findings are written up in Telegram.Benchmarks/README.md. The load-bearing one:
UWP resolves System.Memory's netstandard2.0 asset, so Span<T> is not the runtime's
own and indexing one costs ~10x what the same access over byte[] or byte* costs.
That accounts for the parse being ~3x slower on .NET Native than the desktop
numbers suggested, and it is why Json/TdJsonReader.cs walks raw memory instead:
3.1-3.7x faster than Utf8JsonReader there, with the same allocations.
That reader is a prototype and is not wired into the generator yet. It is
bounds-checked against truncated input, proven by a guard-page sweep over every
prefix of every corpus payload - 70,512 of them - and Validation.cs runs on all
three hosts, so agreement with Utf8JsonReader is checked against the
netstandard2.0 System.Text.Json as well as the net10.0 one.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/emoji-drawer-detach • e7263f1 • 3 files, +37/-1
Detach the emoji and sticker drawers from their collections
InvalidComObjectException — "COM object that has been separated from its
underlying RCW cannot be used" — reported by crash telemetry on 12.9.1,
raised from MvxObservableCollection.ReplaceWith inside
EmojiDrawerViewModel.Update.
EmojiDrawer's CollectionViewSource and its two toolbars bind straight to the
view model's collections, so their native peers subscribe to CollectionChanged.
Deactivate() only called Bindings.StopTracking(), which stops the bindings from
updating but leaves the value they already pushed in place, and it runs right
before XamlMarkupHelper.UnloadObject destroys those peers. The view model
subscribes to the aggregator in its constructor and Handle queues Update on the
UI thread, so a queued update outlives the drawer and raises into the dead
peers.
Clear the collection sources in Deactivate so nothing native is still attached
once the peers go away. StickerDrawer has the same shape and the same terminal
Deactivate contract, so it gets the same treatment.
EmojiMenuFlyout never deactivated its drawer at all, and only one of its three
constructors subscribed to Popup.Closed; move that subscription into Initialize
and tear the drawer down there, matching what StickerPanel does when it unloads
a drawer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Detach the emoji and sticker drawers from their collections
InvalidComObjectException — "COM object that has been separated from its
underlying RCW cannot be used" — reported by crash telemetry on 12.9.1,
raised from MvxObservableCollection.ReplaceWith inside
EmojiDrawerViewModel.Update.
EmojiDrawer's CollectionViewSource and its two toolbars bind straight to the
view model's collections, so their native peers subscribe to CollectionChanged.
Deactivate() only called Bindings.StopTracking(), which stops the bindings from
updating but leaves the value they already pushed in place, and it runs right
before XamlMarkupHelper.UnloadObject destroys those peers. The view model
subscribes to the aggregator in its constructor and Handle queues Update on the
UI thread, so a queued update outlives the drawer and raises into the dead
peers.
Clear the collection sources in Deactivate so nothing native is still attached
once the peers go away. StickerDrawer has the same shape and the same terminal
Deactivate contract, so it gets the same treatment.
EmojiMenuFlyout never deactivated its drawer at all, and only one of its three
constructors subscribed to Popup.Closed; move that subscription into Initialize
and tear the drawer down there, matching what StickerPanel does when it unloads
a drawer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
UnigramDev/Unigram/gallery-close-input • 3dd9796 • 1 files, +39/-6
Stop the gallery from taking input while it closes
Closing the gallery is animated: OnBackRequestedOverride calls Unload()
synchronously, which drops the data context, while Hide() only runs when the
closing animation completes. In between the window is still visible and still
hit-testable, so a click on a caption hyperlink ran Caption_TextEntityClick
with a null ViewModel and crashed in the MessageDelegate constructor
(NullReferenceException, reported by crash telemetry on 12.9.1). The caption
sits outside ScrollingHost, so the tap-to-close handler never consumes it.
Unload() now clears IsHitTestVisible, which closes the whole "input after
unload" family instead of one handler at a time; Caption_TextEntityClick is
guarded as well, because a focused hyperlink can still be invoked from the
keyboard or by automation, neither of which hit tests.
Also fix the same method leaving the window on screen forever: the connected
animation's Completed was subscribed before TryStart, and when TryStart failed
translate became false, so neither Hide() was reachable. Hide() now runs off
the fade batch, which is started on every path, and Completed is subscribed
only when the animation actually started.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
Stop the gallery from taking input while it closes
Closing the gallery is animated: OnBackRequestedOverride calls Unload()
synchronously, which drops the data context, while Hide() only runs when the
closing animation completes. In between the window is still visible and still
hit-testable, so a click on a caption hyperlink ran Caption_TextEntityClick
with a null ViewModel and crashed in the MessageDelegate constructor
(NullReferenceException, reported by crash telemetry on 12.9.1). The caption
sits outside ScrollingHost, so the tap-to-close handler never consumes it.
Unload() now clears IsHitTestVisible, which closes the whole "input after
unload" family instead of one handler at a time; Caption_TextEntityClick is
guarded as well, because a focused hyperlink can still be invoked from the
keyboard or by automation, neither of which hit tests.
Also fix the same method leaving the window on screen forever: the connected
animation's Completed was subscribed before TryStart, and when TryStart failed
translate became false, so neither Hide() was reachable. Hide() now runs off
the fade batch, which is started on every path, and Completed is subscribed
only when the animation actually started.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#unigram
telegramdesktop/tdesktop/nightly • 76ed82b • 2 files, +5/-3
Allowed skipping video cover in document reply preview.
telegramdesktop/tdesktop/nightly • 3bdf527 • 5 files, +29/-0
Added video cover edit actions to send menu.
telegramdesktop/tdesktop/nightly • d668349 • 3 files, +77/-22
Added video cover change support to EditTextMessage.
telegramdesktop/tdesktop/nightly • 943501c • 6 files, +647/-9
Added video cover editing to media edit manager.
telegramdesktop/tdesktop/nightly • 40df230 • 8 files, +71/-20
Added video cover editing to inline message edit controls.
telegramdesktop/tdesktop/nightly • 754fabb • 2 files, +4/-1
Added marked text context to confirm box label.
telegramdesktop/tdesktop/nightly • 585f02f • 2 files, +26/-4
Added passkey name and icon to delete confirmation box.
telegramdesktop/tdesktop/nightly • e395862 • 1 files, +9/-4
Added submit by Enter to security key PIN box.
telegramdesktop/tdesktop/nightly • 570add0 • 1 files, +10/-5
Aligned PIN field with box rows in security key PIN box.
telegramdesktop/tdesktop/nightly • e8f73fb • 2 files, +11/-3
Fixed crash on tab activation before strip sync in profile tabs host.
telegramdesktop/tdesktop/nightly • 4af7d76 • 1 files, +10/-5
Fixed reserved strip height for lent out strip in profile tabs host.
telegramdesktop/tdesktop/nightly • a25c608 • 3 files, +243/-0
Added saved chats tab adapter with message search driver.
telegramdesktop/tdesktop/nightly • 903a2f0 • 2 files, +45/-8
Added saved messages mode to profile top bar.
#tdesktop
Allowed skipping video cover in document reply preview.
telegramdesktop/tdesktop/nightly • 3bdf527 • 5 files, +29/-0
Added video cover edit actions to send menu.
telegramdesktop/tdesktop/nightly • d668349 • 3 files, +77/-22
Added video cover change support to EditTextMessage.
telegramdesktop/tdesktop/nightly • 943501c • 6 files, +647/-9
Added video cover editing to media edit manager.
telegramdesktop/tdesktop/nightly • 40df230 • 8 files, +71/-20
Added video cover editing to inline message edit controls.
telegramdesktop/tdesktop/nightly • 754fabb • 2 files, +4/-1
Added marked text context to confirm box label.
telegramdesktop/tdesktop/nightly • 585f02f • 2 files, +26/-4
Added passkey name and icon to delete confirmation box.
telegramdesktop/tdesktop/nightly • e395862 • 1 files, +9/-4
Added submit by Enter to security key PIN box.
telegramdesktop/tdesktop/nightly • 570add0 • 1 files, +10/-5
Aligned PIN field with box rows in security key PIN box.
telegramdesktop/tdesktop/nightly • e8f73fb • 2 files, +11/-3
Fixed crash on tab activation before strip sync in profile tabs host.
telegramdesktop/tdesktop/nightly • 4af7d76 • 1 files, +10/-5
Fixed reserved strip height for lent out strip in profile tabs host.
telegramdesktop/tdesktop/nightly • a25c608 • 3 files, +243/-0
Added saved chats tab adapter with message search driver.
telegramdesktop/tdesktop/nightly • 903a2f0 • 2 files, +45/-8
Added saved messages mode to profile top bar.
#tdesktop
🫡3
telegramdesktop/tdesktop/nightly • 87a2b06 • 2 files, +44/-23
Added saved messages tabs page to profile section.
telegramdesktop/tdesktop/nightly • 4e8eac7 • 2 files, +5/-4
Routed saved messages info to profile media tabs page.
telegramdesktop/tdesktop/nightly • 4c12446 • 2 files, +4/-1
Added empty field guard for typing send progress in composers.
telegramdesktop/tdesktop/nightly • 385beb2 • 2 files, +82/-7
Added animated round check to filled shapes menu item.
telegramdesktop/tdesktop/nightly • c0fe794 • 2 files, +113/-15
Added radial fill animation to shape icons in shapes menu.
telegramdesktop/tdesktop/nightly • 07da399 • 3 files, +18/-16
Added subpixel precision to dialogs quick action swipe.
telegramdesktop/tdesktop/nightly • 967a02d • 2 files, +12/-3
Added row-scoped repaint for dialogs quick action swipe.
telegramdesktop/tdesktop/nightly • ca067f3 • 2 files, +50/-0
Added pin on top helpers to calls window.
telegramdesktop/tdesktop/nightly • 487472e • 4 files, +135/-2
Added pin on top button to private call panel.
telegramdesktop/tdesktop/nightly • 5352793 • 2 files, +4/-2
Added ability to edit video cover in chats with bots.
telegramdesktop/tdesktop/nightly • ca4c0ae • 1 files, +6/-3
Added width clamp for name and status labels in call panel.
telegramdesktop/tdesktop/nightly • 6a96d9d • 9 files, +382/-5
Moved call button widget from lib_ui to td_ui.
telegramdesktop/tdesktop/nightly • 775c02d • 3 files, +34/-3
Added round mouse hit area to call buttons.
#tdesktop
Added saved messages tabs page to profile section.
telegramdesktop/tdesktop/nightly • 4e8eac7 • 2 files, +5/-4
Routed saved messages info to profile media tabs page.
telegramdesktop/tdesktop/nightly • 4c12446 • 2 files, +4/-1
Added empty field guard for typing send progress in composers.
telegramdesktop/tdesktop/nightly • 385beb2 • 2 files, +82/-7
Added animated round check to filled shapes menu item.
telegramdesktop/tdesktop/nightly • c0fe794 • 2 files, +113/-15
Added radial fill animation to shape icons in shapes menu.
telegramdesktop/tdesktop/nightly • 07da399 • 3 files, +18/-16
Added subpixel precision to dialogs quick action swipe.
telegramdesktop/tdesktop/nightly • 967a02d • 2 files, +12/-3
Added row-scoped repaint for dialogs quick action swipe.
telegramdesktop/tdesktop/nightly • ca067f3 • 2 files, +50/-0
Added pin on top helpers to calls window.
telegramdesktop/tdesktop/nightly • 487472e • 4 files, +135/-2
Added pin on top button to private call panel.
telegramdesktop/tdesktop/nightly • 5352793 • 2 files, +4/-2
Added ability to edit video cover in chats with bots.
telegramdesktop/tdesktop/nightly • ca4c0ae • 1 files, +6/-3
Added width clamp for name and status labels in call panel.
telegramdesktop/tdesktop/nightly • 6a96d9d • 9 files, +382/-5
Moved call button widget from lib_ui to td_ui.
telegramdesktop/tdesktop/nightly • 775c02d • 3 files, +34/-3
Added round mouse hit area to call buttons.
#tdesktop
🫡3
telegramdesktop/tdesktop/nightly • 956cf99 • 4 files, +193/-2
Added hover tooltips for call panel buttons when labels do not fit.
telegramdesktop/tdesktop/nightly • 396fca8 • 1 files, +1/-1
Fixed recursive repaint in toast widget.
telegramdesktop/tdesktop/nightly • 8c00760 • 2 files, +16/-3
Increased mouse priority for recognized text in media viewer.
telegramdesktop/tdesktop/nightly • 881d654 • 1 files, +1/-1
Fixed slideshow controls in exported rich message html.
telegramdesktop/tdesktop/nightly • 464e4f7 • 1 files, +1/-1
Fixed footer color in dark theme of exported rich message html.
telegramdesktop/tdesktop/nightly • 72721f7 • 1 files, +7/-9
Increased font sizes in exported rich message html.
telegramdesktop/tdesktop/nightly • 9b2013f • 8 files, +159/-120
Added optional shortcut for opening article editor.
telegramdesktop/tdesktop/nightly • 16d76be • 2 files, +3/-0
Added text restriction check for article editor in composers.
telegramdesktop/tdesktop/nightly • c03a610 • 2 files, +28/-3
Fixed article editor opening for replies to ephemeral messages.
telegramdesktop/tdesktop/nightly • a77a4bc • 3 files, +36/-4
Fixed article editor window position relative to calling window.
telegramdesktop/tdesktop/nightly • 192510b • 2 files, +10/-0
Added minimal shown time guard for clicks in reactions selector.
telegramdesktop/tdesktop/nightly • 6a5ff36 • 1 files, +3/-0
Fixed stale visible region for grown tab pages in profile tabs host.
telegramdesktop/tdesktop/nightly • 2857245 • 3 files, +52/-0
Moved out media group layout geometry to separate module.
#tdesktop
Added hover tooltips for call panel buttons when labels do not fit.
telegramdesktop/tdesktop/nightly • 396fca8 • 1 files, +1/-1
Fixed recursive repaint in toast widget.
telegramdesktop/tdesktop/nightly • 8c00760 • 2 files, +16/-3
Increased mouse priority for recognized text in media viewer.
telegramdesktop/tdesktop/nightly • 881d654 • 1 files, +1/-1
Fixed slideshow controls in exported rich message html.
telegramdesktop/tdesktop/nightly • 464e4f7 • 1 files, +1/-1
Fixed footer color in dark theme of exported rich message html.
telegramdesktop/tdesktop/nightly • 72721f7 • 1 files, +7/-9
Increased font sizes in exported rich message html.
telegramdesktop/tdesktop/nightly • 9b2013f • 8 files, +159/-120
Added optional shortcut for opening article editor.
telegramdesktop/tdesktop/nightly • 16d76be • 2 files, +3/-0
Added text restriction check for article editor in composers.
telegramdesktop/tdesktop/nightly • c03a610 • 2 files, +28/-3
Fixed article editor opening for replies to ephemeral messages.
telegramdesktop/tdesktop/nightly • a77a4bc • 3 files, +36/-4
Fixed article editor window position relative to calling window.
telegramdesktop/tdesktop/nightly • 192510b • 2 files, +10/-0
Added minimal shown time guard for clicks in reactions selector.
telegramdesktop/tdesktop/nightly • 6a5ff36 • 1 files, +3/-0
Fixed stale visible region for grown tab pages in profile tabs host.
telegramdesktop/tdesktop/nightly • 2857245 • 3 files, +52/-0
Moved out media group layout geometry to separate module.
#tdesktop
🫡3
telegramdesktop/tdesktop/nightly • c3c43a2 • 3 files, +338/-2
Added collage mosaic and slideshow carousel to chat export HTML.
telegramdesktop/tdesktop/nightly • 0f3f761 • 6 files, +142/-0
Added auto scroll on selection drag in translate box and markdown view.
telegramdesktop/tdesktop/nightly • c14dbea • 4 files, +63/-4
Added copy button for translation result to translate box title.
telegramdesktop/tdesktop/nightly • ab46833 • 3 files, +42/-41
Moved imported media resolving from td_iv to editor session.
telegramdesktop/tdesktop/nightly • 915074b • 6 files, +109/-48
Moved editor box and widget data access to editor session.
telegramdesktop/tdesktop/nightly • 3eb9d61 • 15 files, +563/-36
Added HTML flavor with embedded media to rich message copy.
telegramdesktop/tdesktop/nightly • 9b8d55f • 4 files, +90/-35
Added embedded media import from clipboard HTML.
telegramdesktop/tdesktop/nightly • 8cdf3b1 • 4 files, +307/-0
Added marquee label widget with gradient fade and auto scroll.
telegramdesktop/tdesktop/nightly • e9f1f1d • 4 files, +132/-67
[iv-editor] Moved Enter handling into command chain.
telegramdesktop/tdesktop/nightly • a7ad297 • 4 files, +227/-1
[iv-editor] Added list nesting by Tab and Shift+Tab.
telegramdesktop/tdesktop/nightly • a95f7ef • 1 files, +20/-4
[iv-editor] Added list split on Enter in empty item.
telegramdesktop/tdesktop/nightly • 6ee82ed • 1 files, +17/-0
[iv-editor] Added nested list item lift on Backspace.
telegramdesktop/tdesktop/nightly • b1f9e80 • 2 files, +168/-2
[iv-editor] Added joining of next list item on Delete.
#tdesktop
Added collage mosaic and slideshow carousel to chat export HTML.
telegramdesktop/tdesktop/nightly • 0f3f761 • 6 files, +142/-0
Added auto scroll on selection drag in translate box and markdown view.
telegramdesktop/tdesktop/nightly • c14dbea • 4 files, +63/-4
Added copy button for translation result to translate box title.
telegramdesktop/tdesktop/nightly • ab46833 • 3 files, +42/-41
Moved imported media resolving from td_iv to editor session.
telegramdesktop/tdesktop/nightly • 915074b • 6 files, +109/-48
Moved editor box and widget data access to editor session.
telegramdesktop/tdesktop/nightly • 3eb9d61 • 15 files, +563/-36
Added HTML flavor with embedded media to rich message copy.
telegramdesktop/tdesktop/nightly • 9b8d55f • 4 files, +90/-35
Added embedded media import from clipboard HTML.
telegramdesktop/tdesktop/nightly • 8cdf3b1 • 4 files, +307/-0
Added marquee label widget with gradient fade and auto scroll.
telegramdesktop/tdesktop/nightly • e9f1f1d • 4 files, +132/-67
[iv-editor] Moved Enter handling into command chain.
telegramdesktop/tdesktop/nightly • a7ad297 • 4 files, +227/-1
[iv-editor] Added list nesting by Tab and Shift+Tab.
telegramdesktop/tdesktop/nightly • a95f7ef • 1 files, +20/-4
[iv-editor] Added list split on Enter in empty item.
telegramdesktop/tdesktop/nightly • 6ee82ed • 1 files, +17/-0
[iv-editor] Added nested list item lift on Backspace.
telegramdesktop/tdesktop/nightly • b1f9e80 • 2 files, +168/-2
[iv-editor] Added joining of next list item on Delete.
#tdesktop
🫡3
telegramdesktop/tdesktop/nightly • fcdd9e9 • 2 files, +46/-8
[iv-editor] Guarded single form of list item adopting blocks.
telegramdesktop/tdesktop/nightly • 08600f8 • 3 files, +215/-9
[iv-editor] Added pulling of paragraph into list above on Tab.
telegramdesktop/tdesktop/nightly • d528fb3 • 2 files, +52/-0
[iv-editor] Added merging of adjacent lists of same kind.
telegramdesktop/tdesktop/nightly • 7052717 • 1 files, +7/-3
[iv-editor] Added unchecked state for split task items.
telegramdesktop/tdesktop/nightly • aeea752 • 4 files, +126/-4
[iv-editor] Added list input rules with Backspace undo.
telegramdesktop/tdesktop/nightly • 270a399 • 4 files, +77/-1
[iv-editor] Added styled block reset on first Backspace.
telegramdesktop/tdesktop/nightly • a9b2915 • 2 files, +61/-6
[iv-editor] Replaced merge into empty block above with removal.
telegramdesktop/tdesktop/nightly • d333a28 • 3 files, +69/-0
[iv-editor] Added quote exit on Enter in empty last line.
telegramdesktop/tdesktop/nightly • 64f0586 • 2 files, +91/-1
[iv-editor] Added quote exit on Backspace in first line.
telegramdesktop/tdesktop/nightly • d10b922 • 2 files, +66/-3
[iv-editor] Added pulling of block below into quote on Delete.
telegramdesktop/tdesktop/nightly • 008799e • 3 files, +30/-2
[iv-editor] Added heading, quote and code input rules.
telegramdesktop/tdesktop/nightly • 2a0cb17 • 1 files, +0/-3
[iv-editor] Added line inside quote on Enter at its end.
telegramdesktop/tdesktop/nightly • 4e06e31 • 3 files, +146/-3
[iv-editor] Added distribution of selected lines into list items.
#tdesktop
[iv-editor] Guarded single form of list item adopting blocks.
telegramdesktop/tdesktop/nightly • 08600f8 • 3 files, +215/-9
[iv-editor] Added pulling of paragraph into list above on Tab.
telegramdesktop/tdesktop/nightly • d528fb3 • 2 files, +52/-0
[iv-editor] Added merging of adjacent lists of same kind.
telegramdesktop/tdesktop/nightly • 7052717 • 1 files, +7/-3
[iv-editor] Added unchecked state for split task items.
telegramdesktop/tdesktop/nightly • aeea752 • 4 files, +126/-4
[iv-editor] Added list input rules with Backspace undo.
telegramdesktop/tdesktop/nightly • 270a399 • 4 files, +77/-1
[iv-editor] Added styled block reset on first Backspace.
telegramdesktop/tdesktop/nightly • a9b2915 • 2 files, +61/-6
[iv-editor] Replaced merge into empty block above with removal.
telegramdesktop/tdesktop/nightly • d333a28 • 3 files, +69/-0
[iv-editor] Added quote exit on Enter in empty last line.
telegramdesktop/tdesktop/nightly • 64f0586 • 2 files, +91/-1
[iv-editor] Added quote exit on Backspace in first line.
telegramdesktop/tdesktop/nightly • d10b922 • 2 files, +66/-3
[iv-editor] Added pulling of block below into quote on Delete.
telegramdesktop/tdesktop/nightly • 008799e • 3 files, +30/-2
[iv-editor] Added heading, quote and code input rules.
telegramdesktop/tdesktop/nightly • 2a0cb17 • 1 files, +0/-3
[iv-editor] Added line inside quote on Enter at its end.
telegramdesktop/tdesktop/nightly • 4e06e31 • 3 files, +146/-3
[iv-editor] Added distribution of selected lines into list items.
#tdesktop
🫡3