Telegram github commits and releases
4.53K subscribers
601 files
20.3K links
Broadcast from the most important Telegram clients' repositories
Download Telegram
telegramdesktop/tdesktop/devbaa53ca6 files, +60/-6
Support alternative edited-on date display.

telegramdesktop/tdesktop/devcb975041 files, +1/-1
Fix pre-history visibility in manage group.

#tdesktop
🫡4
telegramdesktop/tdesktop/deve98856d2 files, +3/-3
One more attempt to restore context with ANGLE.

#tdesktop
🫡3
morethanwords/tweb/masterbcd27923 files, +67/-1
fix: detect group-call media-transport stall (ICE connected, DTLS hung)

GroupCallInstance.connectionState (and thus the call UI) reports only the
ICE connection state, so a call where ICE reaches `connected` but the
RTCPeerConnection never does — the DTLS handshake stalls, seen on
restrictive networks / some VPNs — looked "connected" while no media ever
flowed: black video tiles, silence, the SFU data channel never opening,
no error anywhere.

Watch the real RTCPeerConnection.connectionState in groupCallsController:
arm a 10s watchdog when ICE connects, and if connectionState hasn't
reached `connected` by then (or goes `failed` — which the existing ICE
`failed` branch never caught, since ICE stays `connected` while DTLS
fails), surface it via the existing conference-bug breadcrumb and hang up
the dead call instead of leaving the user on a silent black call.

Also log the DtlsTransport state on connectionstatechange so an exported
log pins a DTLS stall directly.

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

morethanwords/tweb/master007cf7c13 files, +821/-100
fix: harden E2E call crypto — DH param validation + block authorization

The server is the explicit adversary of Telegram's E2E calls, but WebK trusted
server-supplied data in two places, and the tde2e port had skipped several
validations the reference (tdlib/tdesktop) enforces.

1-on-1 calls (src/lib/crypto):
- Validate the DH config {p,g} from messages.getDhConfig: pin the known 2048-bit
safe prime (safe-prime + generator-congruence fallback) and range-check both
our g_a and the peer's g_b (1<x<p-1, 2^(2048-64) bounds) — mirrors tdesktop
mtproto_dh_utils. Defeats a malicious server's smooth prime (passive key
recovery, invisible to the emoji SAS) and degenerate peer values. Hang up on
any failure.

E2E group calls (src/lib/calls/e2e/blockchain.ts):
- Port tde2e State::apply per-change authorization: derive the signer's
permissions from the prior group state and reject changes it isn't entitled
to make (may_add_users / may_remove_users, no granting flags the signer lacks,
no widening external_permissions, may_change_shared_key requires membership).
- Make state-proof validation faithful to validate_state (group_state/shared_key
omitted-when-changed, present-when-not) and require >=1 SetValue/SetGroupState.

tde2e port hardening:
- TLReader.vector: reject a length larger than the remaining buffer (DoS).
- Reject trailing data after decodeBlock / decodeGroupBroadcast (fetch_end).
- serverToLocal: only strip the +1 magic when magic-1 is a known constructor.
- hydrateStateFromBlock: reject negative block height.
- Buffer future-height verification broadcasts and replay them when the chain
advances (delayed_broadcasts_), so SFU reordering can't suppress the emoji SAS.
- Re-check our pubkey<->user_id on every epoch; co-locate the SELF_PACKET guard.

Adds unit + adversarial tests: DH validation, block authorization, state-proof
shape, and broadcast reordering.

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

#webk
👍3🫡1
morethanwords/tweb/master9d58fe1117 files, +1371/-403
feat: Document Picture-in-Picture — pop the whole client into an always-on-top window

Move the live app DOM into a Document PiP window (same instance, instant) via the left-sidebar More submenu "Picture-in-Picture" entry, gated on Chromium support and toggling to "Exit" while popped out.

Core: appWindow.ts metrics/overlay rebind layer (getAppWindow / getOverlayRoot / bindActiveWindowListener / onAppWindowChange); clientPip move/restore + mirrorDocumentStyles + ClientPipPlaceholder for the vacated tab.

Plus a broad realm-coupling sweep so overlays, context menus, gestures, keyboard, paste/drop, IntersectionObserver, scroll preservation and cross-realm instanceof all keep working when the app lives in the PiP window.

Fullscreen from the media viewer is unavailable in PiP (forbidden by spec and activation cannot cross windows); the button shows a hint to return to the tab instead.

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

morethanwords/tweb/master0fa27412 files, +53/-1
fix: deepEqual no longer leaks ignoreKeys into nested objects

Bug (discovery scan, src/helpers/object/deepEqual.ts)
-----------------------------------------------------
`deepEqual(x, y, ignoreKeys)` is meant to skip the listed keys at the
level being compared (e.g. drafts ignore `date`/`reply_to`; the chat
pattern renderer ignores `element`). But the recursive call passed the
SAME `ignoreKeys` down into every nested object:

ok(x).every((key) => deepEqual(x[key], y[key], ignoreKeys))

so any nested property whose name matched an ignored key was silently
skipped at every depth. Two objects that differed only inside a nested
object — under a same-named key — were wrongly reported equal.

Root cause (current code, line 12)
----------------------------------
`ignoreKeys` was forwarded unconditionally in the recursion, turning a
top-level filter into a global, all-depths filter.

Fix
---
Drop `ignoreKeys` from the recursive call so it applies only at the
comparison (top) level — the documented intent. One-token change plus a
doc note. Both existing callers (drafts `['date','reply_to']`, pattern
renderer `['element']`) compare flat shapes with no nested same-named
keys, so their behavior is unchanged; the fix is strictly more correct
and removes a footgun for any future caller.

Evidence
--------
New src/tests/deepEqual.test.ts (6 tests): flat compare, undefined-prop
skipping, nested recursion, top-level ignore, and the regression case
"does NOT ignore the keys inside nested objects". The regression test
FAILS on master (returns true, expected false) and PASSES after the fix;
the other 5 lock existing behavior. Full suite: 215 passed (the lone
unrelated failure, appGroupCallsManagerJoin.test.ts, is pre-existing on
master and in an off-limits area).

Gates: tsc --noEmit 0 · eslint 0 (both files) · vitest 6/6 green.

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

#webk
👍3🫡2
morethanwords/tweb/master690ab5f4 files, +50/-4
fix: format file sizes >= 1 TB instead of emitting a bare "1.000 "

Bug (class: bugs.telegram.org #5273-adjacent number/size formatting +
direct code defect): formatBytesPure / formatBytes drop the unit for
sizes >= 1 TB.

Root cause: both helpers index a length-4 sizes array
(['B','KB','MB','GB']) with i = floor(log(bytes)/log(1024)). For
bytes >= 1024^4 the index becomes 4 (or more), so sizes[i] is
undefined. formatBytesPure(1024**4) returned "1.000 " (value, a space,
NO unit, trailing space); 5 TB -> "5.000 ". formatBytes was worse: it
passed the undefined LangPackKey straight into i18n(), garbling the
unit. There was also no FileSize.TB lang key.

Reachable from the Data & Storage UI: storageQuota.tsx formats
navigator.storage.estimate() quota, which on a modern multi-TB disk
routinely exceeds 1 TB, plus cumulative cache totals.

Fix: extend the sizes arrays with TB and PB and clamp i to the array
bounds (Math.min(..., sizes.length - 1)) so the index can never select
an undefined unit; add the matching FileSize.TB / FileSize.PB lang keys.

Test (src/tests/formatBytesPure.test.ts): pure-unit Vitest.
Before the fix it FAILED — formatBytesPure(1024**4) === "1.000 " and
5*1024**4 === "5.000 " (bare value, no unit). After the fix it returns
"1.000 TB" / "5.000 TB", and a regression guard asserts the output
always ends in a real unit. Boundary cases (0, 1023, 1024, 1 GB)
unchanged.

Gates: npx tsc --noEmit (0), npx eslint on changed files (0),
npx vitest run src/tests/formatBytesPure.test.ts (7 passed).

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

#webk
🫡3
morethanwords/tweb/master07d14fb7 files, +176/-69
feat: peer-color background-emoji pattern behind web-page previews (tdesktop geometry, shared with replies)

Web-page previews now render the peer-color background-emoji pattern (the sender's
name-color `background_emoji_id`) that tweb already draws behind replies and quotes.
Previously the webpage box got only the quote background + left peer-color border.

Reference (tdesktop): `FillBackgroundEmoji` (history_view_reply.cpp:267) is defined
once and reused by replies (857), contacts (394) AND web pages
(history_view_web_page.cpp:1036) — the same geometry for all; web pages and normal
replies call it with quote=false.

This pulls tdesktop's EXACT pattern geometry into one shared module and points both
replies and web pages at it:
- New `src/components/wrappers/peerColorPattern.ts` owns the pattern (reads the peer's
`color.background_emoji_id`, computes the tint, calls `wrapEmojiPattern`, optional
collectible gift) AND the geometry ported 1:1 from tdesktop — the three cached frame
sizes 12/16/20 (reply.cpp:237-239) across 9 right-anchored positions with per-emoji
opacity (reply.cpp:296-310). tdesktop's right-anchored x maps to tweb's left-origin
canvas as `width - distanceFromRight` (the canvas is CSS-pinned to the box's right
edge); canvas 130x76, emojiSize 24 (source raster >= the largest 20px draw). The
quote-like box's overflow:hidden clips it to the real box height, mirroring tdesktop's
`if(y >= rect.height()) return`.
- `reply.ts` and `bubbles.ts` (webPage box ref) both call `wrapPeerColorPattern` with no
geometry of their own — single source of truth, identical look, exactly as tdesktop
shares one function. Webpage pattern gated on `!isOut && !isSponsored`, peer =
`fwdFromId || fromId` (same as the bubble border, so border + pattern share one color).
- `_chatPinned.scss` `.reply-background-canvas` unified to flush top-right
(top:0; inset-inline-end:0; pointer-events:none) — matching `.webpage-background-canvas`
and tdesktop's right anchor (was top:-6; inset-inline-end:9px).

Note: this is tdesktop's non-quote layout (webpage / contact / normal reply). The
quote-reply variant (+2 emoji, +12px offset, quote=_hasQuoteIcon) is not yet
distinguished in tweb's reply path — a possible follow-up.

Pure logic extracted + unit-tested: `pickPatternEmojiColor.ts` (collectible accent vs
gradient[0], night/dark fallback). Tests: `peerColorPattern.test.ts` 4/4.

Gates: tsc --noEmit clean (touched files); eslint clean; vitest 4/4. Visual verified in
an authed Chrome preview (replies + web-page previews render the identical tdesktop cluster).

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

#webk
🫡3
morethanwords/tweb/mastera2741356 files, +155/-3
feat: render sticker-set / custom-emoji webpage preview as a sticker grid

A webPage linking to a sticker set or custom-emoji set (type
telegram_stickerset) carries a webPageAttributeStickerSet with the set's
preview stickers. tweb mapped the footer button + click (open the set popup)
but never rendered the stickers, so the bubble showed only text + button. All
three official clients draw a small right-aligned square NxN grid in the
square-photo slot (4 -> 2x2, 9 -> 3x3, side = ceil(sqrt(count))). This ports it.

- appWebPagesManager.saveWebPage: normalize the attribute's preview docs through
appDocsManager.saveDoc (MyDocument + file_reference context, like the
webPageAttributeStory precedent) so wrapSticker has media context. Same fix
applied to webPageAttributeStarGiftCollection icons.
- bubbles.ts (messageMediaWebPage): render the square grid into the existing
square-photo preview slot via the existing wrapSticker; custom-emoji + text
color tint gated on pFlags.emojis && pFlags.text_color. Previews loop.
- footer button: a custom-emoji set (pFlags.emojis) says "VIEW EMOJI"
(OpenEmojiSet), a sticker set keeps "VIEW STICKERS" (OpenStickers).
- _chatBubble.scss: grid layout + let the square resizer size to the inline box.
- New pure helper helpers/stickerSetPreviewGrid.ts isolates the grid geometry,
with src/tests/stickerSetPreviewGrid.test.ts (8 cases).

Reference: tdesktop history_view_web_page.cpp:1063-1084; Android (DrKLO)
ChatMessageCell.java:7499-7512.

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

#webk
🫡2👍1
morethanwords/tweb/masterdf939e84 files, +238/-21
feat(reactions): enforce reactions_uniq_max in the reaction picker

The per-user reaction-count cap (reactions_user_max_default / _premium,
"how many distinct reactions ONE user may place on ONE message") is already
fully implemented in tweb — getLimit('reactions') in apiManagerMethods.ts
(maps to those two appConfig keys, premium-aware) is enforced in
appReactionsManager.sendReaction(), which silently drops the user's OLDEST
chosen reaction beyond the cap. This matches tdesktop
(data/data_message_reactions.cpp:1937-2002) and iOS
(MessageReactions.swift:159-164) exactly — no blocking, no toast. Nothing
to build there.

The one reaction cap tweb did NOT implement is reactions_uniq_max
(appConfig.d.ts:27, default 11) — the total number of DISTINCT reactions
allowed to EXIST on a single message. Reference: tdesktop
data/data_peer_values.cpp:86-87 (UniqueReactionsLimit) consumed by
LookupPossibleReactions (data/data_message_reactions.cpp:211-261): once a
message already carries 11 distinct reactions, the picker is narrowed to the
reactions already present on the message, so a user can pile onto an existing
kind but can't introduce a 12th distinct one.

All three official clients enforce this the SAME way — a silent UX gate, no
toast/error — and crucially they gate the custom-emoji search/picker too, not
just the static reaction strip:
- tdesktop: LookupPossibleReactions leaves customAllowed=false at the cap, so
the selector opens in EmojiListMode::RecentReactions and the footer (search +
custom-emoji packs) is never created (history_view_reactions_selector.cpp:1462,
emoji_list_widget.cpp:1627).
- iOS: peerMessageAllowedReactions() returns .set of the present reactions, so
the context menu shows areCustomEmojiEnabled:false and the custom picker is
not offered (TopMessageReactions.swift:48, ChatControllerOpenMessageContextMenu).
- Android: hitLimit → showCustomEmojiReaction()=false hides the (+) expand button
(ReactionsContainerLayout.java:1058,1119,539), and MessageObject.selectReaction()
also returns false for a novel reaction at the cap, counting distinct reactions
EXCLUDING TL_reactionPaid (MessageObject.java:11918-11929).

Implementation:
- Pure helpers in @appManagers/utils/reactions/filterReactionsAtUniqCap:
countDistinctReactions() (excludes reactionPaid, the star counter — matching
Android's chosenCount), isMessageAtUniqReactionCap(), and filterReactionsAtUniqCap
which narrows the offered list to the kinds already present once at the cap
(reactionPaid and already-present reactions kept). Default 11 mirrors tdesktop.
- appReactionsManager.getAvailableReactionsByMessage resolves the warm-cached
appConfig alongside the per-peer reactions (apiManager.getAppConfig, the same
accessor getLimit uses), narrows peerAvailableReactions.reactions, and flags
peerAvailableReactions.atUniqCap from the same uniqMax — single source of truth,
on the picker-open path (not chat-open) so the cached await is fine.
- reactionsMenu hides the custom-emoji search + packs (noSearch = noPacks = true)
when atUniqCap, so a novel reaction can't be introduced via the EmojiTab search
onFinish path — matching the three clients above. The narrowed grid still lets
the user pile onto existing kinds.

Tests: src/tests/filterReactionsAtUniqCap.test.ts — 14 cases covering default
fallback, below-cap pass-through (same reference), at-cap narrowing, reactionPaid
always kept + never counted toward the cap, present custom-emoji kept, custom
uniqMax bounds, empty/undefined inputs, over-cap, and the countDistinctReactions /
isMessageAtUniqReactionCap predicates. All green.

Gates: tsc --noEmit 0 errors; eslint 0 on all 4 files; vitest 14/14 green.

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

#webk
🫡3👍1
morethanwords/tweb/masterf2595117 files, +145/-5
feat: giveaway end-date validation + live, second-accurate date-picker minimum

Giveaway creation (Boosts via Gifts) allowed a past or out-of-window end date,
and the shared date/time picker's "Minimum send time" froze at popup-open so it
drifted into the past. Bound the giveaway end date to [now, now +
giveaway_period_max] like the official clients, and make the picker's minimum
track the clock to the second.

Giveaway end-date validation (boostsViaGifts):
- The expiration picker now passes minDate (today) + minTimeDate (now), so past
days/times are unselectable, plus a defense-in-depth submit guard that
re-validates via isGiveawayUntilDateValid before building the payload
(toast + shake if giveaway_period_max drifted after the date was picked).
- isGiveawayUntilDateValid (new pure helper): untilDate > now && untilDate <=
now + periodMax (seconds). Mirrors tdesktop ChooseDateTimeBox bounds
(.min = now, .max = now + giveawayPeriodMax) and its collect() guard.
- lang: BoostsViaGifts.InvalidEndDate.

Date-picker live minimum (datePicker, benefits every caller — giveaway,
suggested-post, poll end):
- minTimeDate was captured once at open: the caption showed a stale minimum, and
because the displayed floor is minute-granular while the hard guard compared
seconds, the shown minute itself became unselectable once its :00 had elapsed.
- earliestSelectableMinuteMs(minTime, now) (new, @helpers/date): ceil(max(minTime,
now)) up to the next full minute — never behind the clock, never offers a
minute already partly in the past, keeps the caption and Confirm-enable
consistent.
- A 1s clock ticks the minute bucket; the caption, the confirm guard and the
initial input seed all derive from it via the new helper.

Tests: giveawayUntilDate (5 cases) + earliestSelectableMinute (5 cases).

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

#webk
🫡4
morethanwords/tweb/masterda8b3807 files, +351/-345
Regenerate icons

#webk
🫡2
telegramdesktop/tdesktop/dev20542a13 files, +20/-14
Added logs of source path when an unknown deleted message is re-added.

Related commit: 384f6d9a31.

telegramdesktop/tdesktop/dev0cf37dd4 files, +28/-1
Replaced gift button with call button for channel owner in info top bar.

telegramdesktop/tdesktop/dev550f6231 files, +10/-1
Slightly improved trackpad scroll handling in profile flexible top bar.

telegramdesktop/tdesktop/dev16d97065 files, +111/-0
Added playback wave animation around voice message play button.

telegramdesktop/tdesktop/dev92e96441 files, +1/-2
Allowed swipe-to-reply gesture in channels.

telegramdesktop/tdesktop/dev9a38ab72 files, +4/-1
Fixed invisible small group call videos.

telegramdesktop/tdesktop/devc56fd451 files, +2/-2
Fixed repaint region for third column section animation.

telegramdesktop/tdesktop/dev969e84f1 files, +1/-0
Fixed clipped overlay buttons in group call QRhi viewport.

telegramdesktop/tdesktop/dev407d2cf4 files, +10/-0
Fixed new account intro opening in wrong window.

telegramdesktop/tdesktop/devdf64efd6 files, +37/-9
Fixed back from new account intro focusing wrong window.

telegramdesktop/tdesktop/dev5d52a286 files, +66/-2
Added experimental option to keep unmuted unread dialogs on top.

telegramdesktop/tdesktop/deva74ee608 files, +297/-0
Added initial support of swipe-next-channel.

telegramdesktop/tdesktop/dev49396c44 files, +565/-12
Added growing-capsule swipe-next-channel pull indicator with userpic.

#tdesktop
🫡3
telegramdesktop/tdesktop/dev75491857 files, +111/-14
Added bottom-up section slide for swipe-next-channel jump.

telegramdesktop/tdesktop/dev1b886122 files, +22/-10
Fixed stale truncated history view top bar title on peer name change.

telegramdesktop/tdesktop/dev792cc5c1 files, +18/-4
Fixed text recognition overlay rotation in QRhi media viewer.

telegramdesktop/tdesktop/dev2da46cb2 files, +52/-16
Added text recognition for image documents in media viewer.

telegramdesktop/tdesktop/devb9d6b079 files, +628/-17
Added recognized text selection and copying in overlay media viewer.

telegramdesktop/tdesktop/dev05cb1e43 files, +41/-4
Added blockquote and code block rendering to media viewer captions.

telegramdesktop/tdesktop/devd0dba2e7 files, +119/-23
Added text transfer between message field and gif caption box.

telegramdesktop/tdesktop/devd10e9ce8 files, +271/-17
Added alphabetical section headers to contacts list sorted by name.

telegramdesktop/tdesktop/dev076de2d6 files, +498/-0
Added alphabetical fast-scroll index strip to contacts box.

telegramdesktop/tdesktop/devb8e82e31 files, +8/-5
Fixed opening sections from the settings search results.

telegramdesktop/tdesktop/dev165d20a5 files, +106/-15
Added support of deep link copying to settings search results.

telegramdesktop/tdesktop/devc40446b2 files, +13/-4
[thanos] Fixed lost auto-scroll to bottom during animation.

telegramdesktop/tdesktop/dev0ef307f2 files, +29/-17
Added right-button clicks support to pinned bar.

#tdesktop
🫡4👍1
telegramdesktop/tdesktop/dev1ab85976 files, +106/-0
Added reverse iteration over pinned messages on right click.

telegramdesktop/tdesktop/deva1f3ee515 files, +193/-5
Added tooltip above link for hidden forwarded sender.

telegramdesktop/tdesktop/deve0504dd7 files, +763/-1121
[ai] Attempt to automate testing changes.

telegramdesktop/tdesktop/dev9ca694f1 files, +51/-51
Rename currency-balance and post-suggestion strings to Grams.

telegramdesktop/tdesktop/dev40896466 files, +41/-32
Refresh and unify the TON diamond vector icons.

telegramdesktop/tdesktop/devc9ed5ef1 files, +1/-1
Update lib_base.

telegramdesktop/tdesktop/devee39aa34 files, +4/-23
Switch to Qt's queued invoker

#tdesktop
🫡4
telegramdesktop/tdesktop/dev156c8e74 files, +21/-11
Use new base::option::changes in a few old options

telegramdesktop/tdesktop/dev1520a817 files, +7/-7
Bump actions/checkout from 6 to 7

Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
dependency-version: '7'
dependency-type: direct:production
update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

#tdesktop
🫡3💅1
telegramdesktop/tdesktop/dev1d178521 files, +1/-1
Return hinting for math formula fonts.

telegramdesktop/tdesktop/devc8ef31a4 files, +18/-26
Fix rich pages in scheduled / shorcuts sending.

telegramdesktop/tdesktop/dev54a7fd09 files, +101/-24
Use correct table entities limits.

telegramdesktop/tdesktop/devb01d3751 files, +1/-1
Add some more math formula guardrails.

telegramdesktop/tdesktop/devfa9b9265 files, +44/-48
Make InputField menu extension with hooks.

telegramdesktop/tdesktop/dev89977855 files, +81/-31
Enable jump-to-date in forum search.

telegramdesktop/tdesktop/dev2e54c331 files, +0/-2
Fix build with MSVC.

#tdesktop
🫡3
Ajaxy/telegram-tt/masterf45c51b3 files, +4/-4
12.0.30

Ajaxy/telegram-tt/mastercf4b7341 files, +2/-2
Chat Extra: Fix unofficial security risk block (#6977)

Ajaxy/telegram-tt/master0f2a87c1 files, +5/-2
Poll: Disable sending quiz with 1 option (#6980)

Ajaxy/telegram-tt/master7ddb46e3 files, +20/-1
Bot Forum: Change botoforum info button string (#6979)

Ajaxy/telegram-tt/master33ef4fb1 files, +14/-2
Chat List: Fix messege appearance after chat deletion (#6978)

Ajaxy/telegram-tt/master98e7644115 files, +1925/-5812
Migrate to Vite (#6973)

Ajaxy/telegram-tt/master6aa405f6 files, +1654/-156
[CI] Bundle Stats: Add Vite PR reporting (#6976)

Ajaxy/telegram-tt/masterf50eefa7 files, +4305/-6728
Tests: Migrate Jest suite to Vitest (#6981)

Ajaxy/telegram-tt/masterbac8e432 files, +73/-58
PR Stats: Change format (#6983)

Ajaxy/telegram-tt/masteraf5008b26 files, +544/-64
Reaction: Support reaction deletion (#6982)

Ajaxy/telegram-tt/master754be914 files, +90/-23
Vite: Show workers in stats (#7010)

Ajaxy/telegram-tt/master194f19960 files, +836/-745
Remove Buffer dependency (#7009)

Ajaxy/telegram-tt/master49f82ed1 files, +1/-0
Sync: Fix outdated global error (#7014)

Ajaxy/telegram-tt/master38e4b775 files, +15/-29
Safari: Fix several problems (#7015)

Ajaxy/telegram-tt/master9f01ca811 files, +210/-195
GifButton: Move to SCSS Modules (#7013)

#webz
🫡2
Ajaxy/telegram-tt/master48bf95b1 files, +30/-15
Forum: Load topics when listed ids are missing (#7016)

Ajaxy/telegram-tt/masterbee4c3a1 files, +2/-2
Left Main: Fix chat list with tabs layout (#7017)

Ajaxy/telegram-tt/master34474e76 files, +54/-8
Profile: Implement round corners in profile media (#7019)

Ajaxy/telegram-tt/masterf23a4ba3 files, +594/-597
Agents: Move CLAUDE.md to AGENTS.md (#7012)

Ajaxy/telegram-tt/masterd419be911 files, +31/-43
Sticker: Fix blinking on send (#7011)

Ajaxy/telegram-tt/master0b47bec2 files, +2/-2
Audio: Fix audio message transcribing button color (#7021)

Ajaxy/telegram-tt/masterb52deb13 files, +169/-12
Checklists: Fix emoji display for checklists in chat list (#7018)

Ajaxy/telegram-tt/master173280d4 files, +14/-6
Summary: Prevent hiding on error (#7023)

Ajaxy/telegram-tt/master764aaa42 files, +0/-508
Remove unused dependencies (#7024)

Ajaxy/telegram-tt/masterccd4bdc11 files, +0/-88
Remove unused functions (#7025)

Ajaxy/telegram-tt/master9c285e741 files, +156/-4438
Statistics: Upgrade to lovely-chart v2 npm package (#7027)

Ajaxy/telegram-tt/master54185fd7 files, +168/-139
Statistics: Restore lazy lovely-chart loading (#7028)

Ajaxy/telegram-tt/master954f0f1121 files, +7622/-759
Support Rich Messages & Instant View (#6963)

Ajaxy/telegram-tt/master0df85b84 files, +14/-13
Lang: Updates from platform (#7030)

#webz
🫡2
Ajaxy/telegram-tt/master68478f645 files, +615/-401
Redesign: Redesign Left panel and Middle header (#6971)

Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>

Ajaxy/telegram-tt/mastere1f29881 files, +1/-1
Add .kml / .kmz as potentially IP-revealing. (#7031)

Ajaxy/telegram-tt/master73ff15010 files, +76/-63
Rich Messages: Small fixes (#7032)

Ajaxy/telegram-tt/master1fafd661 files, +2/-2
Reactor List Modal: Fix button hover (#7034)

Ajaxy/telegram-tt/master1ead0921 files, +2/-1
Topic: Fix error on min constructor (#7022)

Ajaxy/telegram-tt/masteree72f7110 files, +47/-33
Media Viewer: Fix ghost animation clipping (#7037)

Ajaxy/telegram-tt/master4cd5e768 files, +368/-6
Profile: Show long titles in line (#7020)

Ajaxy/telegram-tt/master3d85e9712 files, +96/-99
Middle Column: Support mask gradient (#7036)

Ajaxy/telegram-tt/master54f469327 files, +153/-74
Toncoin: Rebrand token to Gram

Rename the Toncoin token to "Gram" across user-facing strings and
localization keys, and add the new colored coin icon. The TON blockchain,
the `TON` currency code and protocol/API entities are left intact.

- Update display strings to use the name "Gram"
- Rename token localization keys (`*Ton*` -> `*Gram*`)
- Add `GramIcon` (colored SVG inline, PNG in Settings, `currentColor`
monochrome variant for amount-colored transactions)

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

Ajaxy/telegram-tt/master6f02c7f1 files, +2/-1
Rich Message: Fix more button hover color (#7038)

Ajaxy/telegram-tt/masterbada5e52 files, +20/-0
Modal: Fix Esc handling (#7043)

Ajaxy/telegram-tt/masterb9a888f18 files, +452/-493
Mini Apps: Fix theme color parsing (#7029)

Ajaxy/telegram-tt/masterb7764bc17 files, +51/-30
Search: Fix search input position in chat list (#7040)

#webz
🫡2
Ajaxy/telegram-tt/master5a78bb07 files, +275/-76
Calls: Migrate methods to object parameters (#7044)

Ajaxy/telegram-tt/master248690e1 files, +3/-0
Inline Bots: Support rich content (#7046)

Ajaxy/telegram-tt/master7b64d1b7 files, +15/-8
Styles: Redesign shadows (#7039)

Ajaxy/telegram-tt/master37480bc1 files, +1/-1
Folders: Disable left tab folders grey out in inactive state (#7041)

Ajaxy/telegram-tt/master7a618df2 files, +4/-2
Chat Invite Modal: Redesign (#7047)

Ajaxy/telegram-tt/master9602b8211 files, +96/-89
Revert "Middle Column: Support mask gradient (#7036)" (#7049)

Ajaxy/telegram-tt/master110eb871 files, +1/-1
Files: Fix progress spinner height init (#7052)

Ajaxy/telegram-tt/masterbcdf2a42 files, +1/-5
Contact Modal: Fix top margin for help text in New Contact Modal (#7053)

Ajaxy/telegram-tt/master84ee4479 files, +68/-6
Message: Show edited if needed (#7045)

Ajaxy/telegram-tt/master3f795db31 files, +934/-157
Management: Support guard bots (#7035)

Co-authored-by: Alexander Zinchuk <alx.zinchuk@gmail.com>

Ajaxy/telegram-tt/master0b401d41 files, +4/-0
Quick Preview Modal: Fix layout (#7054)

Ajaxy/telegram-tt/masterfc777e6300 files, +21565/-1530
[Build]

#webz
🫡2
morethanwords/tweb/master11fde946 files, +38/-13
Misc non-sticker fixes: folders sidebar notification badge, media spoiler cover, reactions no-select, CLAUDE.md note, langPack sync

- folders sidebar: reactive total notification count on the menu button (Accessor instead of cloning the toolbar count element)
- media: keep sensitive-content cover clickable during reveal so a repeat click is ignored instead of falling through
- reactions: disable text selection
- CLAUDE.md: add commit-policy note
- regenerate langPack.strings

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

#webk
🫡3
morethanwords/tweb/master550b4883 files, +195/-68
fix: format a shared contact's phone under the viewer's country, not a misread foreign code

Bug (bugs.telegram.org #30681): a phone number in an in-chat shared contact
embed is displayed with the wrong country code when the stored vCard number does
not explicitly contain its dialing prefix. The leading national/city digits are
shown as if they were the country code (does not happen on Android).

Root cause: formatPhoneNumber (src/helpers/formatPhoneNumber.ts) strips every
non-digit and then ALWAYS resolves a country by greedily matching the longest
numeric prefix against every known country code. For a number stored without its
dialing prefix (e.g. an Indonesian national mobile "8123456789"), the leading
"81" collide with Japan (+81): "81" is stripped as the country code and the rest
is regrouped under it, so "8123456789" rendered as "+81 23 4567 89" in the
contact bubble (src/components/chat/bubbles.ts, messageMediaContact). Registered
users (user.phone) always carry their country code, so this only surfaces for the
arbitrary vCard phone_number of a shared contact.

Fix: mirror Android's PhoneFormat, which biases an unprefixed number to the
viewer's own country instead of guessing one from the leading digits.

- formatPhoneNumber takes an opt-in `defaultCountryCode` (the viewer's own
calling code). When the input has no explicit '+' AND its digits do not already
start with that code, the number is treated as a national number of the
viewer's country and grouped with that country's national patterns; `code` is
returned undefined so the caller omits the '+'. Otherwise (explicit '+', or
digits already starting with the viewer's code) the existing greedy detection
runs unchanged, so a registered user's fully-qualified phone and an
international "+..." contact keep their grouping. The grouping logic is factored
into a shared applyPattern() helper.
- bubbles.ts reads the viewer's country code once from the warm main-thread user
cache (apiManagerProxy.getUser(rootScope.myId)) — synchronous, no chat-open
round-trip — and prefixes '+' only when a country code is present. Every other
formatPhoneNumber caller (profile, search, payments, telInputField) is
unchanged.

Result: a same-country contact regains correct grouping (e.g. "+7 999 123-45-67"),
the #30681 number is grouped under the viewer's country with no false foreign code
(Indonesian "8123456789" -> "812 3456789", never "+81 ..."), and a number that
does carry its own code still renders "+CC ...".

Test (src/tests/formatPhoneNumber.test.ts): pure-unit Vitest with two countries
whose leading digits overlap (JP +81, ID +62). Asserts the #30681 input is
grouped under the viewer (+62) with code undefined and never attributed to Japan;
plus the own-country international "+CC", explicit-'+' detection, the
foreign-under-viewer no-"+CC" case, and the unchanged default parse. 6/6 green
(one reproduces the bug before the fix).

Gates: tsc --noEmit 0 errors; eslint 0 on all changed files; vitest 6/6 green.

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

#webk
🫡3