Port a feature from an official Telegram client
This skill is the standard procedure for replicating a feature that exists in an official Telegram client into tweb (Telegram Web K, Solid.js + TS). All three reference clients are cloned locally so they can be grep-ed and read directly — no GitHub round-trips needed.
Reference repositories
The skill expects the three official Telegram clients to be cloned as siblings of the tweb repo (one directory up from the tweb root). Default paths assume ~/Documents/projects/tweb; if the project lives elsewhere, the same sibling layout still applies — just adjust the parent.
| Client |
Default path |
Tech |
Recommended clone |
When to prefer |
| Desktop (tdesktop) |
<projects>/tdesktop |
C++17, Qt |
--depth 1 --filter=blob:none (~85 MB) |
Default if user doesn't specify. Most desktop-aligned UX. Often the cleanest abstraction layer. |
| Android (DrKLO) |
<projects>/Telegram |
Java |
full history (11 GB) or --depth 1 --filter=blob:none (700 MB) |
Mobile-aligned UX, animation timing, gestures. Single huge module — grep first, navigate second. |
| iOS (TelegramMessenger) |
<projects>/Telegram-iOS |
Swift |
--depth 1 --filter=blob:none (~575 MB) |
Best modular separation. Each feature is its own submodule with a self-explanatory name. |
Setup (one-time, only if any repo is missing)
Before invoking the skill, check the paths above exist. If any is missing, clone it as a sibling of tweb:
cd "$(dirname "$(git -C ~/Documents/projects/tweb rev-parse --show-toplevel)")" # or just: cd ~/Documents/projects
git clone --depth 1 --filter=blob:none https://github.com/telegramdesktop/tdesktop.git
git clone --depth 1 --filter=blob:none https://github.com/DrKLO/Telegram.git
git clone --depth 1 --filter=blob:none https://github.com/TelegramMessenger/Telegram-iOS.git
If a clone goes stale, refresh with git -C <path> pull (works for both shallow and full clones). Don't re-clone unless necessary.
Specifying the reference client
The user picks the client; the skill MUST honour that choice.
| User says |
Use |
| "like in iOS", "сделай как в iOS", "из Telegram-iOS" |
iOS |
| "like in tdesktop", "из Desktop", "как в десктопе" |
Desktop |
| "like in Android", "как в Android", "из DrKLO" |
Android |
| "like in the official client", "как у официального клиента" — no specific platform |
Desktop (default), but mention which one was chosen and offer to cross-check another |
| "compare across clients" / "посмотри во всех" |
All three, then summarize differences before implementing |
If the feature renders very differently per platform (e.g., a swipe-driven UI), surface that to the user in 1–2 sentences and ask which one to follow before writing code.
Where common feature areas live
tdesktop — ~/Documents/projects/tdesktop/Telegram/SourceFiles/
| Subdir |
Contains |
api/ |
Manager-like API call wrappers (api_*.cpp — gifts, premium, peer_photo, statistics, …) |
apiwrap.cpp/.h |
Top-level API dispatcher — start here for "what MTProto call does feature X make" |
boxes/ |
Modal dialogs / popups (*_box.cpp) |
chat_helpers/ |
Composer, attachment menu, bots, stickers |
data/ |
Domain models (data_chat, data_messages, data_stories, data_session) |
dialogs/ |
Chat list |
history/ |
Message rendering, reactions, replies |
info/ |
Profile/info/settings screens (per-section subfolders) |
media/ |
Media viewer, audio/video player |
menu/ |
Context menus |
mtproto/ |
Protocol layer |
payments/ |
Stars, gifts, premium, checkout |
settings/ |
Settings UI |
statistics/ |
Channel/group stats |
storage/ |
Local storage wrappers |
passport/, inline_bots/, support/, intro/, iv/, editor/, export/, tde2e/ |
Self-explanatory |
Android DrKLO — ~/Documents/projects/Telegram/TMessagesProj/src/main/java/org/telegram/
| Subdir |
Contains |
messenger/ |
Backend / managers — start here for data flow. Key files: MessagesController.java, MediaDataController.java, ConnectionsManager.java, NotificationCenter.java, MessagesStorage.java, *Controller.java (Stories, Payments, …) |
tgnet/ |
Generated MTProto bindings (TLRPC$*) and protocol layer |
ui/ |
UI surface (Activities & Fragments — huge files like ChatActivity.java, DialogsActivity.java, ProfileActivity.java) |
ui/Cells/ |
List cells |
ui/Components/ |
Custom views |
ui/Stars/, ui/Stories/, ui/Gifts/, ui/Business/ |
Feature-specific UI |
For Android, ALWAYS grep -rn first — the files are too big to read top-to-bottom.
Telegram-iOS — ~/Documents/projects/Telegram-iOS/submodules/
Modular layout. Each module name maps to a feature. Examples:
ChatListUI/ — chat list
ChatPresentationInterfaceState/, ChatMessageInteractiveMediaNode/ — chat interior
StoryContainerScreen/, MediaEditor/ — stories
PremiumUI/, GiftSetupScreen/, StarsUI/ — monetization
PeerInfoUI/, SettingsUI/, PeerInfoScreen/ — info/settings
TelegramCore/ — managers, state, signal pipelines
TelegramApi/ — generated MTProto bindings (Api.functions.*)
TelegramUI/ — top-level UI assembly
Search by feature noun first (ls submodules | grep -i story); the names are descriptive.
Exploration workflow
Locate the feature. For anything beyond a one-line lookup, spawn an Explore subagent scoped to the chosen reference repo so its read-window doesn't blow up the main context:
Agent({
subagent_type: "Explore",
description: "Find gift resale flow in tdesktop",
prompt: "In ~/Documents/projects/tdesktop, find where the 'resell unique gift' flow is implemented. I need: (1) the popup/box class, (2) the MTProto method(s) called and their params, (3) the data model/struct that backs the gift state. Search breadth: medium."
})
For Android use ~/Documents/projects/Telegram/TMessagesProj/src/main/java, for iOS use ~/Documents/projects/Telegram-iOS/submodules.
Find the MTProto method(s). This is the most portable signal across clients — the same method name maps directly to tweb. Search patterns:
- tdesktop:
grep -rn "MTPxxxxxx" Telegram/SourceFiles (e.g. MTPpayments_GetStarGifts)
- Android:
grep -rn "TLRPC\$TL_xxxxxx" TMessagesProj/src/main/java
- iOS:
grep -rn "Api.functions.xxxx" submodules
- tweb equivalent:
await rootScope.managers.apiManager.invokeApi('xxxx', {…})
Read the equivalent area in tweb. tweb's manager pattern (src/lib/appManagers/) maps loosely to tdesktop's Data::* / Android's *Controller / iOS's Telegram* modules. Check whether the feature already has a partial implementation — often a method exists but the UI surface is missing.
Translate, don't transliterate. Their UI code is throwaway for our purposes — re-implement in Solid.js + tweb conventions. What you actually port:
- Data flow: which MTProto methods, in what order, with what params/flags.
- Edge cases: error paths, empty states, retry/throttle/backoff behavior.
- UX shape: when to show what (popup vs page vs inline), button placements, copy.
- NOT their widget tree — Qt / Android Views / SwiftUI don't translate.
Translating tech idioms → tweb
| Other client |
tweb equivalent |
MTP::send(MTPxxxx(…)) (tdesktop), ConnectionsManager.sendRequest(TL_xxxx) (Android), network.request(Api.functions.xxxx) (iOS) |
await rootScope.managers.apiManager.invokeApi('xxxx', {…}) |
Data::Session / MessagesController / TelegramCore.Account |
a manager method on rootScope.managers.app*Manager |
Android NotificationCenter.postNotificationName(…) |
rootScope.dispatchEvent('event_name', …) |
Android NotificationCenter.addObserver(…) / iOS Signal subscription |
rootScope.addEventListener('event_name', handler) |
iOS Signal<T> / Promise<T> |
regular Promise<T> or a Solid signal (createSignal) for reactive UI state |
tdesktop Ui::show(Box<XxxBox>(…)) |
PopupElement subclass under @components/popups (use the procedural showXxxPopup() pattern — see feedback_popup_refactor.md in user memory) |
Android BottomSheet / AlertDialog |
popup or confirmationPopup |
iOS present(controller) / push to nav stack |
popup or new tab via slideTabsAddTab |
tdesktop style::* constants |
SCSS variables in src/scss/ or component-scoped .module.scss |
When official clients disagree
If iOS / Android / Desktop implement the same feature differently (different popup vs page, different option ordering, different default, different MTProto flag values), and the user didn't pre-select a reference:
- Default to tdesktop (canonical-most often).
- In your reply, surface the disagreement in 1–2 sentences with the trade-off, and let the user redirect before you write code.
Example: "iOS shows resale price as a single field; Android splits it into 'price' + 'currency picker'. Defaulting to tdesktop's combined-field approach — say if you'd prefer Android's split UI."
Memory & follow-ups
- After porting a non-trivial feature, consider noting any tweb-specific gotchas in user memory (e.g. "the X manager doesn't expose Y, had to add it") so the next port doesn't re-discover them.
- If the reference client used an MTProto method tweb has never called before, double-check the type in
src/layer.d.ts (@layer) before assuming the call signature.
What this skill does NOT cover
- Pure tweb-internal refactors with no other-client reference.
- MTProto bug debugging — use
tweb-mtproto-debug instead.
- Updating the reference repos themselves —
git pull manually if you want fresher code.
- Reading/scraping non-official Telegram clients (third-party forks, unofficial mods).
1---2name: tg-port-feature3description: Port a feature into tweb (Telegram Web K) using one of the official Telegram clients as a reference implementation. Use when the user asks to implement, port, replicate, or compare a feature that exists in Telegram for iOS, Android (DrKLO), or Desktop (tdesktop) — phrases like "implement X like in iOS / tdesktop / Android", "port X from the official client", "сделай как в iOS / Android / Desktop", "как у официального клиента", "посмотри как в [клиенте]". Also use when investigating how an official client handles a specific MTProto flow, UI behavior, or edge case before writing tweb code. Reference repos are pre-cloned locally — paths are listed inside the skill.4---56# Port a feature from an official Telegram client78This skill is the standard procedure for replicating a feature that exists in an official Telegram client into **tweb** (Telegram Web K, Solid.js + TS). All three reference clients are cloned locally so they can be `grep`-ed and read directly — no GitHub round-trips needed.910## Reference repositories1112The skill expects the three official Telegram clients to be cloned as **siblings of the tweb repo** (one directory up from the tweb root). Default paths assume `~/Documents/projects/tweb`; if the project lives elsewhere, the same sibling layout still applies — just adjust the parent.1314| Client | Default path | Tech | Recommended clone | When to prefer |15|---|---|---|---|---|16| **Desktop** (tdesktop) | `<projects>/tdesktop` | C++17, Qt | `--depth 1 --filter=blob:none` (~85 MB) | Default if user doesn't specify. Most desktop-aligned UX. Often the cleanest abstraction layer. |17| **Android** (DrKLO) | `<projects>/Telegram` | Java | full history (~11 GB) or `--depth 1 --filter=blob:none` (~700 MB) | Mobile-aligned UX, animation timing, gestures. Single huge module — `grep` first, navigate second. |18| **iOS** (TelegramMessenger) | `<projects>/Telegram-iOS` | Swift | `--depth 1 --filter=blob:none` (~575 MB) | Best modular separation. Each feature is its own submodule with a self-explanatory name. |1920### Setup (one-time, only if any repo is missing)2122Before invoking the skill, check the paths above exist. If any is missing, clone it as a sibling of `tweb`:2324```bash25cd "$(dirname "$(git -C ~/Documents/projects/tweb rev-parse --show-toplevel)")" # or just: cd ~/Documents/projects26git clone --depth 1 --filter=blob:none https://github.com/telegramdesktop/tdesktop.git27git clone --depth 1 --filter=blob:none https://github.com/DrKLO/Telegram.git28git clone --depth 1 --filter=blob:none https://github.com/TelegramMessenger/Telegram-iOS.git29```3031If a clone goes stale, refresh with `git -C <path> pull` (works for both shallow and full clones). Don't re-clone unless necessary.3233## Specifying the reference client3435The user picks the client; the skill MUST honour that choice.3637| User says | Use |38|---|---|39| "like in iOS", "сделай как в iOS", "из Telegram-iOS" | **iOS** |40| "like in tdesktop", "из Desktop", "как в десктопе" | **Desktop** |41| "like in Android", "как в Android", "из DrKLO" | **Android** |42| "like in the official client", "как у официального клиента" — no specific platform | **Desktop** (default), but mention which one was chosen and offer to cross-check another |43| "compare across clients" / "посмотри во всех" | All three, then summarize differences before implementing |4445If the feature renders very differently per platform (e.g., a swipe-driven UI), surface that to the user in 1–2 sentences and ask which one to follow before writing code.4647## Where common feature areas live4849### tdesktop — `~/Documents/projects/tdesktop/Telegram/SourceFiles/`5051| Subdir | Contains |52|---|---|53| `api/` | Manager-like API call wrappers (`api_*.cpp` — gifts, premium, peer_photo, statistics, …) |54| `apiwrap.cpp/.h` | Top-level API dispatcher — start here for "what MTProto call does feature X make" |55| `boxes/` | Modal dialogs / popups (`*_box.cpp`) |56| `chat_helpers/` | Composer, attachment menu, bots, stickers |57| `data/` | Domain models (`data_chat`, `data_messages`, `data_stories`, `data_session`) |58| `dialogs/` | Chat list |59| `history/` | Message rendering, reactions, replies |60| `info/` | Profile/info/settings screens (per-section subfolders) |61| `media/` | Media viewer, audio/video player |62| `menu/` | Context menus |63| `mtproto/` | Protocol layer |64| `payments/` | Stars, gifts, premium, checkout |65| `settings/` | Settings UI |66| `statistics/` | Channel/group stats |67| `storage/` | Local storage wrappers |68| `passport/`, `inline_bots/`, `support/`, `intro/`, `iv/`, `editor/`, `export/`, `tde2e/` | Self-explanatory |6970### Android DrKLO — `~/Documents/projects/Telegram/TMessagesProj/src/main/java/org/telegram/`7172| Subdir | Contains |73|---|---|74| `messenger/` | Backend / managers — start here for data flow. Key files: `MessagesController.java`, `MediaDataController.java`, `ConnectionsManager.java`, `NotificationCenter.java`, `MessagesStorage.java`, `*Controller.java` (Stories, Payments, …) |75| `tgnet/` | Generated MTProto bindings (`TLRPC$*`) and protocol layer |76| `ui/` | UI surface (Activities & Fragments — huge files like `ChatActivity.java`, `DialogsActivity.java`, `ProfileActivity.java`) |77| `ui/Cells/` | List cells |78| `ui/Components/` | Custom views |79| `ui/Stars/`, `ui/Stories/`, `ui/Gifts/`, `ui/Business/` | Feature-specific UI |8081For Android, ALWAYS `grep -rn` first — the files are too big to read top-to-bottom.8283### Telegram-iOS — `~/Documents/projects/Telegram-iOS/submodules/`8485Modular layout. Each module name maps to a feature. Examples:86- `ChatListUI/` — chat list87- `ChatPresentationInterfaceState/`, `ChatMessageInteractiveMediaNode/` — chat interior88- `StoryContainerScreen/`, `MediaEditor/` — stories89- `PremiumUI/`, `GiftSetupScreen/`, `StarsUI/` — monetization90- `PeerInfoUI/`, `SettingsUI/`, `PeerInfoScreen/` — info/settings91- `TelegramCore/` — managers, state, signal pipelines92- `TelegramApi/` — generated MTProto bindings (`Api.functions.*`)93- `TelegramUI/` — top-level UI assembly9495Search by feature noun first (`ls submodules | grep -i story`); the names are descriptive.9697## Exploration workflow98991. **Locate the feature.** For anything beyond a one-line lookup, spawn an `Explore` subagent scoped to the chosen reference repo so its read-window doesn't blow up the main context:100101 ```102 Agent({103 subagent_type: "Explore",104 description: "Find gift resale flow in tdesktop",105 prompt: "In ~/Documents/projects/tdesktop, find where the 'resell unique gift' flow is implemented. I need: (1) the popup/box class, (2) the MTProto method(s) called and their params, (3) the data model/struct that backs the gift state. Search breadth: medium."106 })107 ```108109 For Android use `~/Documents/projects/Telegram/TMessagesProj/src/main/java`, for iOS use `~/Documents/projects/Telegram-iOS/submodules`.1101112. **Find the MTProto method(s).** This is the most portable signal across clients — the same method name maps directly to tweb. Search patterns:112 - tdesktop: `grep -rn "MTPxxxxxx" Telegram/SourceFiles` (e.g. `MTPpayments_GetStarGifts`)113 - Android: `grep -rn "TLRPC\$TL_xxxxxx" TMessagesProj/src/main/java`114 - iOS: `grep -rn "Api.functions.xxxx" submodules`115 - tweb equivalent: `await rootScope.managers.apiManager.invokeApi('xxxx', {…})`1161173. **Read the equivalent area in tweb.** tweb's manager pattern (`src/lib/appManagers/`) maps loosely to tdesktop's `Data::*` / Android's `*Controller` / iOS's `Telegram*` modules. Check whether the feature already has a partial implementation — often a method exists but the UI surface is missing.1181194. **Translate, don't transliterate.** Their UI code is throwaway for our purposes — re-implement in Solid.js + tweb conventions. What you actually port:120 - **Data flow**: which MTProto methods, in what order, with what params/flags.121 - **Edge cases**: error paths, empty states, retry/throttle/backoff behavior.122 - **UX shape**: when to show what (popup vs page vs inline), button placements, copy.123 - **NOT** their widget tree — Qt / Android Views / SwiftUI don't translate.124125## Translating tech idioms → tweb126127| Other client | tweb equivalent |128|---|---|129| `MTP::send(MTPxxxx(…))` (tdesktop), `ConnectionsManager.sendRequest(TL_xxxx)` (Android), `network.request(Api.functions.xxxx)` (iOS) | `await rootScope.managers.apiManager.invokeApi('xxxx', {…})` |130| `Data::Session` / `MessagesController` / `TelegramCore.Account` | a manager method on `rootScope.managers.app*Manager` |131| Android `NotificationCenter.postNotificationName(…)` | `rootScope.dispatchEvent('event_name', …)` |132| Android `NotificationCenter.addObserver(…)` / iOS Signal subscription | `rootScope.addEventListener('event_name', handler)` |133| iOS `Signal<T>` / `Promise<T>` | regular `Promise<T>` or a Solid signal (`createSignal`) for reactive UI state |134| tdesktop `Ui::show(Box<XxxBox>(…))` | `PopupElement` subclass under `@components/popups` (use the procedural `showXxxPopup()` pattern — see `feedback_popup_refactor.md` in user memory) |135| Android `BottomSheet` / `AlertDialog` | popup or `confirmationPopup` |136| iOS `present(controller)` / push to nav stack | popup or new tab via `slideTabsAddTab` |137| tdesktop `style::*` constants | SCSS variables in `src/scss/` or component-scoped `.module.scss` |138139## When official clients disagree140141If iOS / Android / Desktop implement the same feature differently (different popup vs page, different option ordering, different default, different MTProto flag values), and the user didn't pre-select a reference:1421431. Default to **tdesktop** (canonical-most often).1442. In your reply, surface the disagreement in 1–2 sentences with the trade-off, and let the user redirect before you write code.145146Example: "iOS shows resale price as a single field; Android splits it into 'price' + 'currency picker'. Defaulting to tdesktop's combined-field approach — say if you'd prefer Android's split UI."147148## Memory & follow-ups149150- After porting a non-trivial feature, consider noting any tweb-specific gotchas in user memory (e.g. "the X manager doesn't expose Y, had to add it") so the next port doesn't re-discover them.151- If the reference client used an MTProto method tweb has never called before, double-check the type in `src/layer.d.ts` (`@layer`) before assuming the call signature.152153## What this skill does NOT cover154155- Pure tweb-internal refactors with no other-client reference.156- MTProto bug debugging — use `tweb-mtproto-debug` instead.157- Updating the reference repos themselves — `git pull` manually if you want fresher code.158- Reading/scraping non-official Telegram clients (third-party forks, unofficial mods).