morethanwords/tweb/master • 29e1272 • 6 files, +351/-350
Regenerate icons
morethanwords/tweb/master • 3853912 • 300 files, +934/-818
Build
#webk
Regenerate icons
morethanwords/tweb/master • 3853912 • 300 files, +934/-818
Build
#webk
🫡3
Ajaxy/telegram-tt/master • 2b0f5d9 • 3 files, +4/-4
12.0.31
Ajaxy/telegram-tt/master • 297afa4 • 1 files, +5/-2
Message List: Fix random scroll to bottom on open (#7055)
Ajaxy/telegram-tt/master • 5d4d237 • 1 files, +5/-1
Bot: Hide info immediately when starting new chat (#7056)
Ajaxy/telegram-tt/master • 1dc28e3 • 2 files, +11/-3
Polls: Add line break for poll action messages (#7051)
Ajaxy/telegram-tt/master • 81ee4be • 34 files, +1444/-873
Poll: Support more creation options (#7033)
Ajaxy/telegram-tt/master • 72557c2 • 6 files, +91/-115
Poll Answer Results: Redesign (#7057)
Ajaxy/telegram-tt/master • acebf11 • 1 files, +12/-0
Media Viewer: Fix closing MV on Esc (#7062)
Ajaxy/telegram-tt/master • 46fa678 • 10 files, +669/-0
Message: Support read metrics (#7060)
Ajaxy/telegram-tt/master • 7899098 • 1 files, +1/-0
Middle Column: Fix visibility after unlock (#7065)
Ajaxy/telegram-tt/master • 7f3f7ff • 4 files, +18/-10
Middle Header: Fix middle header panel styles (#7063)
Ajaxy/telegram-tt/master • d97d4da • 94 files, +207/-193
[Build]
#webz
12.0.31
Ajaxy/telegram-tt/master • 297afa4 • 1 files, +5/-2
Message List: Fix random scroll to bottom on open (#7055)
Ajaxy/telegram-tt/master • 5d4d237 • 1 files, +5/-1
Bot: Hide info immediately when starting new chat (#7056)
Ajaxy/telegram-tt/master • 1dc28e3 • 2 files, +11/-3
Polls: Add line break for poll action messages (#7051)
Ajaxy/telegram-tt/master • 81ee4be • 34 files, +1444/-873
Poll: Support more creation options (#7033)
Ajaxy/telegram-tt/master • 72557c2 • 6 files, +91/-115
Poll Answer Results: Redesign (#7057)
Ajaxy/telegram-tt/master • acebf11 • 1 files, +12/-0
Media Viewer: Fix closing MV on Esc (#7062)
Ajaxy/telegram-tt/master • 46fa678 • 10 files, +669/-0
Message: Support read metrics (#7060)
Ajaxy/telegram-tt/master • 7899098 • 1 files, +1/-0
Middle Column: Fix visibility after unlock (#7065)
Ajaxy/telegram-tt/master • 7f3f7ff • 4 files, +18/-10
Middle Header: Fix middle header panel styles (#7063)
Ajaxy/telegram-tt/master • d97d4da • 94 files, +207/-193
[Build]
#webz
🫡2
morethanwords/tweb/master • ef8c380 • 1 files, +2/-4
Merge branch 'feature/ai-editor' of github.com:H7GhosT/private-tweb into feature/ai-editor
morethanwords/tweb/master • 50bb529 • 300 files, +10771/-2264
Merge branch 'master' into feature/ai-editor
morethanwords/tweb/master • d88da87 • 1 files, +2/-5
Change comment
morethanwords/tweb/master • c381bf7 • 1 files, +2/-2
Prevent EmojiDocumentIcon rerendering when color changes
morethanwords/tweb/master • 1d339ba • 5 files, +125/-49
Make inner content scrollable
morethanwords/tweb/master • dc3cec8 • 1 files, +1/-5
Change arrow bg hover color
morethanwords/tweb/master • 8b0c60e • 1 files, +5/-1
Fix skeleton color on light theme
morethanwords/tweb/master • e4d5364 • 2 files, +21/-4
Recompose message when editing currently selected tone
morethanwords/tweb/master • 299f8ab • 1 files, +8/-7
Move create style button to the end of the list
morethanwords/tweb/master • 8fedf41 • 3 files, +22/-3
Fade original and result scrollables edges when scrolled
morethanwords/tweb/master • 1743807 • 3 files, +17/-2
Hide thumb when original is collapsed
morethanwords/tweb/master • 68c84cc • 3 files, +6/-6
Show created by you in view tone popup
morethanwords/tweb/master • 4d9db4b • 2 files, +21/-25
Fix formatting
morethanwords/tweb/master • d25787e • 4 files, +128/-21
Add send menu to the send button
morethanwords/tweb/master • d3d34f3 • 19 files, +880/-684
Remake the translation popup to be like the AI Editor popup
#webk
Merge branch 'feature/ai-editor' of github.com:H7GhosT/private-tweb into feature/ai-editor
morethanwords/tweb/master • 50bb529 • 300 files, +10771/-2264
Merge branch 'master' into feature/ai-editor
morethanwords/tweb/master • d88da87 • 1 files, +2/-5
Change comment
morethanwords/tweb/master • c381bf7 • 1 files, +2/-2
Prevent EmojiDocumentIcon rerendering when color changes
morethanwords/tweb/master • 1d339ba • 5 files, +125/-49
Make inner content scrollable
morethanwords/tweb/master • dc3cec8 • 1 files, +1/-5
Change arrow bg hover color
morethanwords/tweb/master • 8b0c60e • 1 files, +5/-1
Fix skeleton color on light theme
morethanwords/tweb/master • e4d5364 • 2 files, +21/-4
Recompose message when editing currently selected tone
morethanwords/tweb/master • 299f8ab • 1 files, +8/-7
Move create style button to the end of the list
morethanwords/tweb/master • 8fedf41 • 3 files, +22/-3
Fade original and result scrollables edges when scrolled
morethanwords/tweb/master • 1743807 • 3 files, +17/-2
Hide thumb when original is collapsed
morethanwords/tweb/master • 68c84cc • 3 files, +6/-6
Show created by you in view tone popup
morethanwords/tweb/master • 4d9db4b • 2 files, +21/-25
Fix formatting
morethanwords/tweb/master • d25787e • 4 files, +128/-21
Add send menu to the send button
morethanwords/tweb/master • d3d34f3 • 19 files, +880/-684
Remake the translation popup to be like the AI Editor popup
#webk
🫡3
morethanwords/tweb/master • 7f76e50 • 3 files, +17/-8
Rethrow error for copyTextToClipboard
morethanwords/tweb/master • 3dfc632 • 2 files, +0/-2
Remove unnecessary instance check
morethanwords/tweb/master • f15ee7b • 3 files, +16/-22
Small refactor. Fix Original's overlay fading when appearing first time
morethanwords/tweb/master • 0cf0e02 • 3 files, +69/-12
Add animation to scrollable's mask. Trigger onSizeChange when scrollable
is added to DOM.
morethanwords/tweb/master • 4281569 • 1 files, +7/-4
AI editor: don't leak composer's pending silent/schedule/effect into sends
getMessageSendingParams() carries the chat input's live silent/scheduleDate/
scheduleRepeatPeriod/effect (e.g. restored from a draft). The AI editor manages
these via its own send menu, so override them outright instead of merging —
otherwise a message sent from the editor inherits state the editor never showed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • f382c99 • 32 files, +1260/-777
Merge pull request #678 from H7GhosT/feature/ai-editor
Ai Editor improvements
#webk
Rethrow error for copyTextToClipboard
morethanwords/tweb/master • 3dfc632 • 2 files, +0/-2
Remove unnecessary instance check
morethanwords/tweb/master • f15ee7b • 3 files, +16/-22
Small refactor. Fix Original's overlay fading when appearing first time
morethanwords/tweb/master • 0cf0e02 • 3 files, +69/-12
Add animation to scrollable's mask. Trigger onSizeChange when scrollable
is added to DOM.
morethanwords/tweb/master • 4281569 • 1 files, +7/-4
AI editor: don't leak composer's pending silent/schedule/effect into sends
getMessageSendingParams() carries the chat input's live silent/scheduleDate/
scheduleRepeatPeriod/effect (e.g. restored from a draft). The AI editor manages
these via its own send menu, so override them outright instead of merging —
otherwise a message sent from the editor inherits state the editor never showed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • f382c99 • 32 files, +1260/-777
Merge pull request #678 from H7GhosT/feature/ai-editor
Ai Editor improvements
#webk
🫡3
morethanwords/tweb/master • e9ea7d0 • 1 files, +4/-0
Ignore clicks on pinned gifts when profile avatar is expanded
The pinned-gifts layer paints on top of the avatar and was only hidden
via opacity:0 when the header is expanded, which leaves it catching
clicks meant for the avatar underneath. Make it pointer-events:none in
the expanded state so clicks fall through to the avatar handler.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 0b2e4a5 • 1 files, +9/-2
Fix player menus invisible in fullscreen
Playback-rate / quality / live menus open via ButtonMenuToggle, which
portals the menu to document.body. While the player wrapper is the
fullscreen element the browser paints only its subtree (top layer), so a
menu in document.body never shows. Mount the menu inside the fullscreen
element when the trigger lives there; falls back to the overlay root
otherwise.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • bc0fe18 • 3 files, +27/-19
Chat preview: apply per-chat theme to outgoing bubbles + fix bubble width
A preview Chat sets isPreview, so Chat.handleBackgrounds() short-circuits and
applyContainerTheme() never runs — the per-chat theme's outgoing-bubble vars
(--message-out-*) were missing on the container, so outgoing bubbles fell back
to the global :root palette ("theme only partially applied"). Apply the
resolved theme to the preview's chat container, re-firing on day/night toggle,
mirroring Chat._handleBackgrounds.
Also scope the bubbles scroll-area negative inset-inline rule to #column-center
so it no longer widens the preview popup's bubbles, and drop the now-redundant
inset-inline: 0 override in _chatPreview.scss.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 2010d64 • 3 files, +56/-0
Close global search and left-sidebar tabs when switching folders
Switching a chat folder now dismisses an open global search and closes any
stacked left-sidebar tabs, mirroring closeEverythingInside. Tabs close the
natural way (as if via the back arrow), so a tab that needs confirmation
still asks; if the user declines, the folder switch is cancelled. Both folder
UIs (the horizontal tabs and the vertical folders sidebar) funnel through
selectFolderByIndex, so a single hook covers both.
Adds SidebarSlider.closeAllTabsNaturally (close tabs top-down honoring each
tab's isConfirmationNeededOnClose) and AppSidebarLeft.closeEverythingInsideNaturally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Ignore clicks on pinned gifts when profile avatar is expanded
The pinned-gifts layer paints on top of the avatar and was only hidden
via opacity:0 when the header is expanded, which leaves it catching
clicks meant for the avatar underneath. Make it pointer-events:none in
the expanded state so clicks fall through to the avatar handler.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 0b2e4a5 • 1 files, +9/-2
Fix player menus invisible in fullscreen
Playback-rate / quality / live menus open via ButtonMenuToggle, which
portals the menu to document.body. While the player wrapper is the
fullscreen element the browser paints only its subtree (top layer), so a
menu in document.body never shows. Mount the menu inside the fullscreen
element when the trigger lives there; falls back to the overlay root
otherwise.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • bc0fe18 • 3 files, +27/-19
Chat preview: apply per-chat theme to outgoing bubbles + fix bubble width
A preview Chat sets isPreview, so Chat.handleBackgrounds() short-circuits and
applyContainerTheme() never runs — the per-chat theme's outgoing-bubble vars
(--message-out-*) were missing on the container, so outgoing bubbles fell back
to the global :root palette ("theme only partially applied"). Apply the
resolved theme to the preview's chat container, re-firing on day/night toggle,
mirroring Chat._handleBackgrounds.
Also scope the bubbles scroll-area negative inset-inline rule to #column-center
so it no longer widens the preview popup's bubbles, and drop the now-redundant
inset-inline: 0 override in _chatPreview.scss.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 2010d64 • 3 files, +56/-0
Close global search and left-sidebar tabs when switching folders
Switching a chat folder now dismisses an open global search and closes any
stacked left-sidebar tabs, mirroring closeEverythingInside. Tabs close the
natural way (as if via the back arrow), so a tab that needs confirmation
still asks; if the user declines, the folder switch is cancelled. Both folder
UIs (the horizontal tabs and the vertical folders sidebar) funnel through
selectFolderByIndex, so a single hook covers both.
Adds SidebarSlider.closeAllTabsNaturally (close tabs top-down honoring each
tab's isConfirmationNeededOnClose) and AppSidebarLeft.closeEverythingInsideNaturally.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡3
morethanwords/tweb/master • d232274 • 1 files, +10/-12
Fix voice waveform dropped for non-standard (62-byte) waveforms
decodeWaveform read each 5-bit sample via DataView.getUint16, which
over-reads one byte past the needed one. For a 62-byte / 99-sample
waveform the last sample's read landed out of bounds, threw a
RangeError, and the catch returned an empty array — so the whole
waveform silently failed to render (flat line). Standard 63-byte
waveforms have a spare trailing byte and happened to work.
Read the two bytes manually with a bounds-guarded high byte instead;
output is bit-identical for 63-byte waveforms and now correct for
shorter ones. Also fixes the latent byteOffset bug in the old
new DataView(waveform.buffer) on view-backed arrays.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 29ce0b6 • 5 files, +106/-16
Pause video avatars when not visible (incl. right sidebar closed)
Video avatars kept animating while off-screen and, notably, while sitting in
the right sidebar after it was slid closed — the column is hidden with a
transform but stays mounted, so an IntersectionObserver never reports it as
hidden.
- avatarNew: register avatar <video>s with animationIntersector instead of an
ad-hoc IntersectionObserver (pauses on off-screen / blur / idle / lite-mode,
resumes when visible, survives Document-PiP). Covers both the chat-list /
topbar overlay and the profile-carousel / service-bubble path, which had no
visibility gating at all.
- animationIntersector: add toggleVideosUnder(element, paused) to pause+lock
(or resume) every registered video inside a container.
- sidebarRight: pause/resume the column's avatar videos on hide()/show(), since
the transform-based hide is invisible to the observer.
- peerProfileAvatars: self-suspend the tab-progress rAF loop when the active
video isn't advancing (restart on a captured 'play' / carousel nav), and free
the carousel's registered videos on cleanup so a video locked while the
sidebar was closed can't leak when the profile is torn down.
- createLoopingMutedVideo: set autoplay only after the start-frame seek so the
clip isn't briefly flashed from frame 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 388a8b4 • 32 files, +1260/-777
Merge branch 'master' of https://github.com/morethanwords/tweb
#webk
Fix voice waveform dropped for non-standard (62-byte) waveforms
decodeWaveform read each 5-bit sample via DataView.getUint16, which
over-reads one byte past the needed one. For a 62-byte / 99-sample
waveform the last sample's read landed out of bounds, threw a
RangeError, and the catch returned an empty array — so the whole
waveform silently failed to render (flat line). Standard 63-byte
waveforms have a spare trailing byte and happened to work.
Read the two bytes manually with a bounds-guarded high byte instead;
output is bit-identical for 63-byte waveforms and now correct for
shorter ones. Also fixes the latent byteOffset bug in the old
new DataView(waveform.buffer) on view-backed arrays.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 29ce0b6 • 5 files, +106/-16
Pause video avatars when not visible (incl. right sidebar closed)
Video avatars kept animating while off-screen and, notably, while sitting in
the right sidebar after it was slid closed — the column is hidden with a
transform but stays mounted, so an IntersectionObserver never reports it as
hidden.
- avatarNew: register avatar <video>s with animationIntersector instead of an
ad-hoc IntersectionObserver (pauses on off-screen / blur / idle / lite-mode,
resumes when visible, survives Document-PiP). Covers both the chat-list /
topbar overlay and the profile-carousel / service-bubble path, which had no
visibility gating at all.
- animationIntersector: add toggleVideosUnder(element, paused) to pause+lock
(or resume) every registered video inside a container.
- sidebarRight: pause/resume the column's avatar videos on hide()/show(), since
the transform-based hide is invisible to the observer.
- peerProfileAvatars: self-suspend the tab-progress rAF loop when the active
video isn't advancing (restart on a captured 'play' / carousel nav), and free
the carousel's registered videos on cleanup so a video locked while the
sidebar was closed can't leak when the profile is torn down.
- createLoopingMutedVideo: set autoplay only after the start-frame seek so the
clip isn't briefly flashed from frame 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 388a8b4 • 32 files, +1260/-777
Merge branch 'master' of https://github.com/morethanwords/tweb
#webk
🫡4
morethanwords/tweb/master • e8c9dca • 1 files, +2/-2
Skip post-send handling when no message was actually sent
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 64efd76 • 1 files, +1/-1
Wrap long bot verification text in peer profile
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 330ca41 • 1 files, +8/-1
Show profile Edit button for any admin, not only change_info
The Edit (pencil) button in group/channel info was gated solely on the
change_info right, so an admin who could manage members/permissions/admins
but lacked change_info couldn't reach the edit screen — even though that
screen already gates each section on its own rights and merely locks the
name field when change_info is missing.
Mirror tdesktop's EditPeerInfoBox::Available: show the button to any admin
(channel: just_admin || change_info, excluding monoforums; basic group:
change_info || change_permissions).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Skip post-send handling when no message was actually sent
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 64efd76 • 1 files, +1/-1
Wrap long bot verification text in peer profile
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 330ca41 • 1 files, +8/-1
Show profile Edit button for any admin, not only change_info
The Edit (pencil) button in group/channel info was gated solely on the
change_info right, so an admin who could manage members/permissions/admins
but lacked change_info couldn't reach the edit screen — even though that
screen already gates each section on its own rights and merely locks the
name field when change_info is missing.
Mirror tdesktop's EditPeerInfoBox::Available: show the button to any admin
(channel: just_admin || change_info, excluding monoforums; basic group:
change_info || change_permissions).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡4💅1
morethanwords/tweb/master • 6cb2df2 • 1 files, +2/-2
Don't append live messages to the pinned-messages view
The history_append/history_multiappend handlers guarded against the
Scheduled/Static/Logs chat types but not Pinned. The pinned view shares the
chat's messagesStorageKey and, once fully loaded, has loadedAll.bottom === true,
so a regular incoming message fell through every guard and got rendered as a
bubble in the pinned list. Exclude Pinned from both append handlers, matching
the existing pattern; history_delete is left intact so deleted/unpinned messages
still disappear from the view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 5d5c3ef • 3 files, +49/-3
Read mention/reaction when jumping to an already-visible message
The unread mention/reaction corner button only scrolled to the target via
setMessageId; the actual read is driven solely by the intersection observer,
which fires only on an intersection CHANGE. When the target is already on
screen — typical for reactions, which sit on our own recent messages — the
programmatic scroll is a no-op, so no callback ever fires: the reaction stays
unread and the badge never clears.
Fix it by re-arming the observer for the focused bubble after it is rendered
and scrolled into view, forcing a fresh intersection entry. It still reads
only if the bubble is actually intersecting, so "read == seen" holds.
- SuperIntersectionObserver.reobserve(target): re-deliver an entry for an
already-observed target (handles bubbles with multiple callbacks, e.g. a
mention that also carries the history observer); no-op while frozen.
- ChatBubbles.reobserveUnreadContent(peerId, mid): guarded re-arm, only acts
on a bubble still pending unread content (idempotent with the natural flow).
- ChatInput: await the inner render/scroll promise before re-arming; poll
votes keep their own read flow in goToNextMention.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Don't append live messages to the pinned-messages view
The history_append/history_multiappend handlers guarded against the
Scheduled/Static/Logs chat types but not Pinned. The pinned view shares the
chat's messagesStorageKey and, once fully loaded, has loadedAll.bottom === true,
so a regular incoming message fell through every guard and got rendered as a
bubble in the pinned list. Exclude Pinned from both append handlers, matching
the existing pattern; history_delete is left intact so deleted/unpinned messages
still disappear from the view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 5d5c3ef • 3 files, +49/-3
Read mention/reaction when jumping to an already-visible message
The unread mention/reaction corner button only scrolled to the target via
setMessageId; the actual read is driven solely by the intersection observer,
which fires only on an intersection CHANGE. When the target is already on
screen — typical for reactions, which sit on our own recent messages — the
programmatic scroll is a no-op, so no callback ever fires: the reaction stays
unread and the badge never clears.
Fix it by re-arming the observer for the focused bubble after it is rendered
and scrolled into view, forcing a fresh intersection entry. It still reads
only if the bubble is actually intersecting, so "read == seen" holds.
- SuperIntersectionObserver.reobserve(target): re-deliver an entry for an
already-observed target (handles bubbles with multiple callbacks, e.g. a
mention that also carries the history observer); no-op while frozen.
- ChatBubbles.reobserveUnreadContent(peerId, mid): guarded re-arm, only acts
on a bubble still pending unread content (idempotent with the natural flow).
- ChatInput: await the inner render/scroll promise before re-arming; poll
votes keep their own read flow in goToNextMention.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡3
morethanwords/tweb/master • fcdcb04 • 3 files, +338/-0
Refresh chat-folder membership on unread-mark toggle and message deletion
Chat-folder membership is driven by a cached index (dialog[index_N] /
folder.dialogs) that is written only by dialogsStorage.processDialogForFilters
— separate from the folder unread counters. Two update handlers mutated a
dialog's unread state without re-processing filters, so a dialog could go stale
in an exclude_read ("Unread") folder: it showed in the list with no badge (the
badge reads the live unread_count, already 0) and only vanished when opened
(open -> readHistory -> processDialogForFilters).
- onUpdateDialogUnreadMark: marking a read chat unread didn't add it to an
exclude_read folder, and clearing the mark (e.g. from another client) left it
stranded there.
- onUpdateDeleteMessages: deleting a channel's unread posts drops unread_count
to 0 but left the channel stuck in the folder.
Both fixed by calling processDialogForFilters after releaseUnreadCount, matching
the readHistory ordering (release -> processDialogForFilters -> dispatch). No
counter double-count: the unread*PeerIds sets are idempotent and the additive
unreadMessagesCount nets to zero (release covers folders that matched before the
change; the membership-change modify uses the post-mutate count).
Adds synthetic regression tests:
- unreadMarkFilterStale.test.ts: mark/clear maintains the index, counters net-zero.
- channelMuteFilter.test.ts: muting alone is correct in isolation; deleting a
channel's unread posts must drop it from exclude_read.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • f9eb2de • 8 files, +350/-12
Fix forum topic open, invite-tab, and cross-account propagation bugs
- Opening a just-created topic in a cold (not-yet-warm) forum dropped the
threadId and showed the forum root: Chat.setPeer set isForum only in the
async onChangePeer, so a chat_update mid-transition compared against a stale
isForum and reset the peer. Set isForum synchronously in setPeer.
- Joining a forum via an invite link didn't open the topics tab. Route both
PopupJoinChatInvite.openChat and internalLinkProcessor's already-joined
(chatInviteAlready/Peek) path through appImManager.open() instead of
setInnerPeer(), so forums go through op() and open the sidebar topics tab.
- A topic created on another account never appeared in an open/cached topic
list, and stayed hidden even after reopening or receiving messages, until a
full reload. Replication-lag race: the topic-create arrives before the topic
is queryable by id, messages.getForumTopicsByID returns forumTopicDeleted,
and getForumTopicById blacklisted it permanently in deletedTopics. Build the
topic locally from the messageActionTopicCreate action (it carries title,
icon and id) instead of fetching it, and only blacklist a topic when the
server explicitly returns forumTopicDeleted (not on errors/omissions).
- Guard canManageTopic and the generic-animations sticker preload against null.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Refresh chat-folder membership on unread-mark toggle and message deletion
Chat-folder membership is driven by a cached index (dialog[index_N] /
folder.dialogs) that is written only by dialogsStorage.processDialogForFilters
— separate from the folder unread counters. Two update handlers mutated a
dialog's unread state without re-processing filters, so a dialog could go stale
in an exclude_read ("Unread") folder: it showed in the list with no badge (the
badge reads the live unread_count, already 0) and only vanished when opened
(open -> readHistory -> processDialogForFilters).
- onUpdateDialogUnreadMark: marking a read chat unread didn't add it to an
exclude_read folder, and clearing the mark (e.g. from another client) left it
stranded there.
- onUpdateDeleteMessages: deleting a channel's unread posts drops unread_count
to 0 but left the channel stuck in the folder.
Both fixed by calling processDialogForFilters after releaseUnreadCount, matching
the readHistory ordering (release -> processDialogForFilters -> dispatch). No
counter double-count: the unread*PeerIds sets are idempotent and the additive
unreadMessagesCount nets to zero (release covers folders that matched before the
change; the membership-change modify uses the post-mutate count).
Adds synthetic regression tests:
- unreadMarkFilterStale.test.ts: mark/clear maintains the index, counters net-zero.
- channelMuteFilter.test.ts: muting alone is correct in isolation; deleting a
channel's unread posts must drop it from exclude_read.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • f9eb2de • 8 files, +350/-12
Fix forum topic open, invite-tab, and cross-account propagation bugs
- Opening a just-created topic in a cold (not-yet-warm) forum dropped the
threadId and showed the forum root: Chat.setPeer set isForum only in the
async onChangePeer, so a chat_update mid-transition compared against a stale
isForum and reset the peer. Set isForum synchronously in setPeer.
- Joining a forum via an invite link didn't open the topics tab. Route both
PopupJoinChatInvite.openChat and internalLinkProcessor's already-joined
(chatInviteAlready/Peek) path through appImManager.open() instead of
setInnerPeer(), so forums go through op() and open the sidebar topics tab.
- A topic created on another account never appeared in an open/cached topic
list, and stayed hidden even after reopening or receiving messages, until a
full reload. Replication-lag race: the topic-create arrives before the topic
is queryable by id, messages.getForumTopicsByID returns forumTopicDeleted,
and getForumTopicById blacklisted it permanently in deletedTopics. Build the
topic locally from the messageActionTopicCreate action (it carries title,
icon and id) instead of fetching it, and only blacklist a topic when the
server explicitly returns forumTopicDeleted (not on errors/omissions).
- Guard canManageTopic and the generic-animations sticker preload against null.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡3
morethanwords/tweb/master • e95fe52 • 1 files, +5/-1
Keep personal account in paid-reaction send-as list
channels.getSendAs(for_paid_reactions=true) omits the personal account;
the client injects it as the default peer. But this.sendAsPeers — the
canonical pool every click handler rebuilds the menu from — was sliced
before that injection, so the personal account was missing from it. The
menu rendered correctly on first open (built from the local list), but
the first selection regenerated it from this.sendAsPeers and dropped the
personal account permanently. Save this.sendAsPeers after folding in the
current/default peer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 07f7288 • 9 files, +214/-51
Notify when another user posts a story
Add a local notification when a peer you follow posts a new story, mirroring the message-notification pipeline: the worker dispatches a notificationBuild task carrying a story payload (discriminated union) and the main thread builds and shows it. Respects the existing per-peer/global stories_muted setting, skips own/out/already-read stories, and covers the uncached-peer path so a peer's very first story still notifies.
Story notifications play no sound and don't bump the tab-title counter (noIncrement). Clicking opens the peer's stories at the first unread (matching Android and the avatar tap): in-page directly, on the service-worker/background-push path via a custom.story_id marker, and cross-account via a new 'story' hash param.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Keep personal account in paid-reaction send-as list
channels.getSendAs(for_paid_reactions=true) omits the personal account;
the client injects it as the default peer. But this.sendAsPeers — the
canonical pool every click handler rebuilds the menu from — was sliced
before that injection, so the personal account was missing from it. The
menu rendered correctly on first open (built from the local list), but
the first selection regenerated it from this.sendAsPeers and dropped the
personal account permanently. Save this.sendAsPeers after folding in the
current/default peer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 07f7288 • 9 files, +214/-51
Notify when another user posts a story
Add a local notification when a peer you follow posts a new story, mirroring the message-notification pipeline: the worker dispatches a notificationBuild task carrying a story payload (discriminated union) and the main thread builds and shows it. Respects the existing per-peer/global stories_muted setting, skips own/out/already-read stories, and covers the uncached-peer path so a peer's very first story still notifies.
Story notifications play no sound and don't bump the tab-title counter (noIncrement). Clicking opens the peer's stories at the first unread (matching Android and the avatar tap): in-page directly, on the service-worker/background-push path via a custom.story_id marker, and cross-account via a new 'story' hash param.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡3
telegramdesktop/tdesktop/dev • 4943c41 • 9 files, +16/-38
Get rid of new Q_OS_LINUX
telegramdesktop/tdesktop/dev • 3447020 • 1 files, +3/-2
Make macOS packaged cache key more stable
telegramdesktop/tdesktop/dev • 22143a8 • 1 files, +15/-3
Per-week cache key for ccache on Linux action
telegramdesktop/tdesktop/dev • 734d709 • 3 files, +32/-1
Allow to override ffmpeg thread count for debugging purposes
#tdesktop
Get rid of new Q_OS_LINUX
telegramdesktop/tdesktop/dev • 3447020 • 1 files, +3/-2
Make macOS packaged cache key more stable
telegramdesktop/tdesktop/dev • 22143a8 • 1 files, +15/-3
Per-week cache key for ccache on Linux action
telegramdesktop/tdesktop/dev • 734d709 • 3 files, +32/-1
Allow to override ffmpeg thread count for debugging purposes
#tdesktop
🫡3
telegramdesktop/tdesktop/dev • d6f2e4c • 1 files, +2/-4
Simplify the use of new ffmpeg thread option
#tdesktop
Simplify the use of new ffmpeg thread option
#tdesktop
🫡2
telegramdesktop/tdesktop/dev • 5e1a05f • 1 files, +1/-1
Fix crash when removing pinned chats during folder changes.
#tdesktop
Fix crash when removing pinned chats during folder changes.
#tdesktop
🫡2
telegramdesktop/tdesktop/dev • 24ccd1c • 1 files, +6/-1
Fix crash when finishing the appearing text animation
textAppearCheckLine() runs synchronously from inside the width and
height animation tick callbacks (textAppearWidthCallback /
textAppearHeightCallback). Calling RemoveComponents(TextAppearing::Bit())
there destroys those very animations (and their Ui::Animations::Simple
data) while the animations manager is still iterating them, leading to a
use-after-free / null _data dereference in Simple::value().
Defer the component removal to the next main loop iteration so the
running tick can unwind first.
#tdesktop
Fix crash when finishing the appearing text animation
textAppearCheckLine() runs synchronously from inside the width and
height animation tick callbacks (textAppearWidthCallback /
textAppearHeightCallback). Calling RemoveComponents(TextAppearing::Bit())
there destroys those very animations (and their Ui::Animations::Simple
data) while the animations manager is still iterating them, leading to a
use-after-free / null _data dereference in Simple::value().
Defer the component removal to the next main loop iteration so the
running tick can unwind first.
#tdesktop
🫡2
telegramdesktop/tdesktop/dev • 7f8c690 • 1 files, +0/-1
Remove duplicate QT_NO_PRIVATE_MODULE_WARNING from root CMakeLists.txt
It now lives in cmake/external/qt/package.cmake
#tdesktop
Remove duplicate QT_NO_PRIVATE_MODULE_WARNING from root CMakeLists.txt
It now lives in cmake/external/qt/package.cmake
#tdesktop
🫡2
telegramdesktop/tdesktop/dev • df268f3 • 4 files, +10/-10
Bump actions/cache from 5 to 6
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)
---
updated-dependencies:
- dependency-name: actions/cache
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
#tdesktop
Bump actions/cache from 5 to 6
Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v5...v6)
---
updated-dependencies:
- dependency-name: actions/cache
dependency-version: '6'
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
#tdesktop
🫡2
telegramdesktop/tdesktop/dev • 12ce38f • 1 files, +25/-4
Fixed editing of selected caption in album to target its own media.
telegramdesktop/tdesktop/dev • a5142b9 • 5 files, +94/-34
Slightly improved filling of swipe-next-channel gesture.
telegramdesktop/tdesktop/dev • 76120b7 • 6 files, +186/-175
Replaced custom scroll processing in profile top bar with regular one.
telegramdesktop/tdesktop/dev • a4ed19f • 1 files, +21/-7
Extended regular scroll processing to stories section.
telegramdesktop/tdesktop/dev • ffa6386 • 5 files, +137/-5
Cached peer list rows into opaque images while scrolling.
telegramdesktop/tdesktop/dev • fe8ca5a • 2 files, +34/-4
Added separate painting of peer badge emoji status to peer list.
telegramdesktop/tdesktop/dev • 1fb28f6 • 2 files, +14/-1
Added separate painting of peer list row emoji status to peer list.
telegramdesktop/tdesktop/dev • 89b4a78 • 2 files, +22/-1
Allowed emoji status animation over cached peer list rows.
telegramdesktop/tdesktop/dev • 3a90101 • 2 files, +45/-0
Added refresh of loaded userpics over cached peer list rows.
telegramdesktop/tdesktop/dev • 55b7484 • 3 files, +382/-113
Added media view video as system media controls source.
telegramdesktop/tdesktop/dev • 43b2110 • 3 files, +153/-1
Added metadata of media view video playback to system media controls.
telegramdesktop/tdesktop/dev • 40b3f62 • 3 files, +80/-7
Added swipe-back gesture to info profile section.
telegramdesktop/tdesktop/dev • f362c16 • 1 files, +2/-1
Fixed instant Cmd+Up/Down reply in sections with compose controls.
#tdesktop
Fixed editing of selected caption in album to target its own media.
telegramdesktop/tdesktop/dev • a5142b9 • 5 files, +94/-34
Slightly improved filling of swipe-next-channel gesture.
telegramdesktop/tdesktop/dev • 76120b7 • 6 files, +186/-175
Replaced custom scroll processing in profile top bar with regular one.
telegramdesktop/tdesktop/dev • a4ed19f • 1 files, +21/-7
Extended regular scroll processing to stories section.
telegramdesktop/tdesktop/dev • ffa6386 • 5 files, +137/-5
Cached peer list rows into opaque images while scrolling.
telegramdesktop/tdesktop/dev • fe8ca5a • 2 files, +34/-4
Added separate painting of peer badge emoji status to peer list.
telegramdesktop/tdesktop/dev • 1fb28f6 • 2 files, +14/-1
Added separate painting of peer list row emoji status to peer list.
telegramdesktop/tdesktop/dev • 89b4a78 • 2 files, +22/-1
Allowed emoji status animation over cached peer list rows.
telegramdesktop/tdesktop/dev • 3a90101 • 2 files, +45/-0
Added refresh of loaded userpics over cached peer list rows.
telegramdesktop/tdesktop/dev • 55b7484 • 3 files, +382/-113
Added media view video as system media controls source.
telegramdesktop/tdesktop/dev • 43b2110 • 3 files, +153/-1
Added metadata of media view video playback to system media controls.
telegramdesktop/tdesktop/dev • 40b3f62 • 3 files, +80/-7
Added swipe-back gesture to info profile section.
telegramdesktop/tdesktop/dev • f362c16 • 1 files, +2/-1
Fixed instant Cmd+Up/Down reply in sections with compose controls.
#tdesktop
🫡3
telegramdesktop/tdesktop/dev • 14909bc • 2 files, +6/-4
Fixed display of sticker-set and favorite actions for sending stickers.
telegramdesktop/tdesktop/dev • 85c9495 • 7 files, +184/-0
Added keyboard extension of message text selection to history sections.
#tdesktop
Fixed display of sticker-set and favorite actions for sending stickers.
telegramdesktop/tdesktop/dev • 85c9495 • 7 files, +184/-0
Added keyboard extension of message text selection to history sections.
#tdesktop
🫡2
morethanwords/tweb/master • 512d07d • 1 files, +25/-2
Fix stories hold-to-pause not resuming on release
SwipeHandler bound its global mouseup/touchend listeners to
this.element.ownerDocument at construction time. For a Solid JSX
element (cloned from a <template>), ownerDocument is the inert
about:blank template-contents document until the node is inserted
into the live DOM. The stories viewer constructs the SwipeHandler
before its <Portal> mounts the element, so the release listeners
were bound to a document that never receives real events: mousedown
(bound to the element directly) still paused on press, but mouseup
never reached reset()/onReset()/play(), so the story stayed paused.
Bind the gesture-end listener lazily in handleStart instead, when the
element is live and ownerDocument resolves to the real (main or
Document-PiP) document. Keeps the PiP fix from 9d58fe155 intact and
mirrors the lazy pattern already used in attachGrabListeners.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • bbd3782 • 3 files, +87/-79
Render topic profile avatar in the profile-avatars header
Forum and bot-forum topic profiles used a separate inline 'simple avatar'
(.profile-avatar.avatar-120 inside the MainSection, gated by has-simple-avatar).
Route them through the same PeerProfileAvatars header as regular chats with no
photo instead: PeerProfileAvatars.setPeer now takes a threadId, skips the photo
carousel/list-loader, and renders the topic icon as a single centered avatar-120
(no profile-avatars-avatar-fake duplicate, no scaled-down full photo).
- peerProfileAvatars: topic branch in setPeer; processItem renders avatar-120
with a 120px custom-emoji size for topics.
- peerProfile: always use the carousel path, passing the topic id; drop the
needSimpleAvatar concept and the in-section avatar.
- _profile.scss: .is-topic centers the fixed-size icon and keeps it unscaled
when the header collapses; removed the dead .has-simple-avatar rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 1296446 • 2 files, +23/-25
Fix sponsored topbar plate not reserving chat padding-top
The sponsored/ad plate is the only floating plate with height: 'auto', so setFloating measures its offsetHeight. The measurement ran inside the count loop, before the wrapper's `hide` (display: none) class was toggled off — and a display:none ancestor zeroes descendants' offsetHeight. So exactly when the ad plate is the one flipping the visible-plate count 0 -> 1, it was measured at 0 and never reserved its padding-top, leaving it overlapping the chat. Compute the visible set and un-hide the wrapper before measuring.
Also clarify in updatePinnedFloatingHeight that a mid-scroll position is left anchored on purpose (matches iOS/Android: the plate slides over the content rather than shoving it down); behavior unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Fix stories hold-to-pause not resuming on release
SwipeHandler bound its global mouseup/touchend listeners to
this.element.ownerDocument at construction time. For a Solid JSX
element (cloned from a <template>), ownerDocument is the inert
about:blank template-contents document until the node is inserted
into the live DOM. The stories viewer constructs the SwipeHandler
before its <Portal> mounts the element, so the release listeners
were bound to a document that never receives real events: mousedown
(bound to the element directly) still paused on press, but mouseup
never reached reset()/onReset()/play(), so the story stayed paused.
Bind the gesture-end listener lazily in handleStart instead, when the
element is live and ownerDocument resolves to the real (main or
Document-PiP) document. Keeps the PiP fix from 9d58fe155 intact and
mirrors the lazy pattern already used in attachGrabListeners.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • bbd3782 • 3 files, +87/-79
Render topic profile avatar in the profile-avatars header
Forum and bot-forum topic profiles used a separate inline 'simple avatar'
(.profile-avatar.avatar-120 inside the MainSection, gated by has-simple-avatar).
Route them through the same PeerProfileAvatars header as regular chats with no
photo instead: PeerProfileAvatars.setPeer now takes a threadId, skips the photo
carousel/list-loader, and renders the topic icon as a single centered avatar-120
(no profile-avatars-avatar-fake duplicate, no scaled-down full photo).
- peerProfileAvatars: topic branch in setPeer; processItem renders avatar-120
with a 120px custom-emoji size for topics.
- peerProfile: always use the carousel path, passing the topic id; drop the
needSimpleAvatar concept and the in-section avatar.
- _profile.scss: .is-topic centers the fixed-size icon and keeps it unscaled
when the header collapses; removed the dead .has-simple-avatar rule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • 1296446 • 2 files, +23/-25
Fix sponsored topbar plate not reserving chat padding-top
The sponsored/ad plate is the only floating plate with height: 'auto', so setFloating measures its offsetHeight. The measurement ran inside the count loop, before the wrapper's `hide` (display: none) class was toggled off — and a display:none ancestor zeroes descendants' offsetHeight. So exactly when the ad plate is the one flipping the visible-plate count 0 -> 1, it was measured at 0 and never reserved its padding-top, leaving it overlapping the chat. Compute the visible set and un-hide the wrapper before measuring.
Also clarify in updatePinnedFloatingHeight that a mid-scroll position is left anchored on purpose (matches iOS/Android: the plate slides over the content rather than shoving it down); behavior unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡3
morethanwords/tweb/master • 96fb8c0 • 4 files, +227/-194
Fix intermittent SRP 2FA password rejection (unpadded A)
The client public value A = g^a mod p was serialized with its minimal
big-endian length instead of being zero-padded to 256 bytes. The server
normalizes A to 256 bytes before hashing u and M1, so whenever g^a mod p
had a leading zero byte (~1/256 of attempts) our M1 mismatched the
server's and a correct password was rejected at random; a retry (fresh
account.getPassword -> new a) usually succeeded. Pad A to 256 bytes
everywhere, exactly like tdlib's A_bn.to_binary(256).
While matching tdlib's PasswordManager::get_input_check_password:
- generate the ephemeral secret a from a fresh local CSPRNG instead of
the server-provided state.secure_random (which leaked the secret
exponent and was constant across the now-removed retry loop);
- add the validations that were commented-out stubs: verifyDhPrimeAndGenerator
(= check_config) for both the check and new-password paths, and the
srp_B range/length check (0 < B < p, 248 <= len <= 256).
Rewrite src/tests/srp.test.ts into a real suite: KDF vs an independent
Node-crypto reference, a client/server SRP round-trip x25, a forced
leading-zero-A regression (mutation-confirmed it fails on the unpadded
code), the isNew verifier path, and validation throws. Drop the stale
A/M1/srp_B fixtures and the dead src/mock/srp_original.ts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • c6fb69c • 2 files, +1/-8
Align reply-quote poll icon inline instead of flexing the subtitle
The poll-option checkmark in the reply/pinned container now carries the
standard inline-icon classes (inline-icon inline-icon-left) so it aligns
with the quote text through the normal inline flow, dropping the bespoke
.with-icon flex wrapper on the subtitle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
Fix intermittent SRP 2FA password rejection (unpadded A)
The client public value A = g^a mod p was serialized with its minimal
big-endian length instead of being zero-padded to 256 bytes. The server
normalizes A to 256 bytes before hashing u and M1, so whenever g^a mod p
had a leading zero byte (~1/256 of attempts) our M1 mismatched the
server's and a correct password was rejected at random; a retry (fresh
account.getPassword -> new a) usually succeeded. Pad A to 256 bytes
everywhere, exactly like tdlib's A_bn.to_binary(256).
While matching tdlib's PasswordManager::get_input_check_password:
- generate the ephemeral secret a from a fresh local CSPRNG instead of
the server-provided state.secure_random (which leaked the secret
exponent and was constant across the now-removed retry loop);
- add the validations that were commented-out stubs: verifyDhPrimeAndGenerator
(= check_config) for both the check and new-password paths, and the
srp_B range/length check (0 < B < p, 248 <= len <= 256).
Rewrite src/tests/srp.test.ts into a real suite: KDF vs an independent
Node-crypto reference, a client/server SRP round-trip x25, a forced
leading-zero-A regression (mutation-confirmed it fails on the unpadded
code), the isNew verifier path, and validation throws. Drop the stale
A/M1/srp_B fixtures and the dead src/mock/srp_original.ts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
morethanwords/tweb/master • c6fb69c • 2 files, +1/-8
Align reply-quote poll icon inline instead of flexing the subtitle
The poll-option checkmark in the reply/pinned container now carries the
standard inline-icon classes (inline-icon inline-icon-left) so it aligns
with the quote text through the normal inline flow, dropping the bespoke
.with-icon flex wrapper on the subtitle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#webk
🫡3
🫡2