

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# Agent Guide for Telegram Desktop23This guide defines repository-wide instructions for coding agents working with the Telegram Desktop codebase.45## Working from Codex on Windows + WSL67This checkout may be opened in Codex Desktop through the Windows UNC path `\\wsl.localhost\{distro}\home\{user}\Telegram\tdesktop`, while the real Linux path is `/home/{user}/Telegram/tdesktop`. Treat it as a WSL/Linux checkout first, not as a native Windows checkout.89- Prefer running repository-aware commands through WSL:1011```powershell12wsl.exe -d {distro} --cd /home/{user}/Telegram/tdesktop -- <command>13```1415- PowerShell can read and write files through the UNC path, but native Windows tools may see different ownership, path, executable, or line-ending behavior than Linux tools.16- Git from PowerShell over `\\wsl.localhost\...` can fail with `detected dubious ownership`. Use WSL Git instead. Do not change global Git `safe.directory` settings unless the user explicitly asks for that.17- Keep path styles matched to the shell. Use `/home/{user}/Telegram/tdesktop/...` with WSL commands, and quoted `\\wsl.localhost\{distro}\home\{user}\Telegram\tdesktop\...` paths with native Windows commands. Avoid passing UNC paths to Linux tools or Linux paths to native Windows tools unless the tool explicitly supports them.18- If a command behaves strangely from the PowerShell UNC working directory, retry the same command through `wsl.exe -d {distro} --cd /home/{user}/Telegram/tdesktop -- ...` before concluding the repository or command is broken.19- Recursive searches and repo inspection are usually faster and more faithful through WSL, for example `wsl.exe -d {distro} --cd /home/{user}/Telegram/tdesktop -- rg ...`.20- Do not assume the WSL host has the build toolchain installed directly. In this setup, WSL may not have `cmake`, while Windows may have `cmake`, and the configured `out/` tree may still target the Linux Docker toolchain. Do not run native Windows `cmake --build out` against a Linux/Docker build tree.21- For WSL/Linux builds, use the Docker build entry point from the repository root: `Telegram/build/docker/centos_env/build_debug.sh`. The Docker daemon must be reachable from WSL; checking `docker info` is fine, but do not start a build unless the user asked for one.22- Existing build outputs may be Linux binaries, for example `out/Debug/Telegram` as an ELF executable, not `Telegram.exe`. Verify the build tree before assuming which platform produced it.23- Be careful with text file line endings. In a WSL/Linux checkout, files should remain LF-only unless the file already uses another convention. CRLF finishing applies only to native, non-WSL Windows runs/checkouts. Do not let PowerShell or Windows tools silently rewrite WSL files to CRLF. If a file becomes mixed, normalize it back to the convention appropriate for the current checkout, without adding a UTF-8 BOM.24- When using the local `perform-task` skill from this WSL checkout, keep external AI task artifacts and edited project text files LF-only. Treat its Windows text-normalization phase as not applicable to WSL, except to record that line endings were checked and kept LF/no-BOM. Run CRLF normalization only in a native, non-WSL Windows checkout.2526## Build System Structure2728The build system expects this directory layout:2930```text31L:\Telegram\ # BuildPath32L:\Telegram\tdesktop\ # Repository (you work here)33L:\Telegram\Libraries\ # 32-bit dependencies (Linux/macOS)34L:\Telegram\win64\Libraries\ # 64-bit dependencies (Windows)35L:\Telegram\ThirdParty\ # Build tools (NuGet, Python, etc.)36```3738Dependencies are located relative to the repository: `../Libraries`, `../win64/Libraries`, or `../ThirdParty`.3940## Build Configuration4142### Build Commands4344**From repository root, run:**4546```bash47cmake --build out --config Debug --target Telegram48```4950That's it. The `out/` directory is already configured. The executable will be at `out/Debug/Telegram.exe`.5152**From WSL, run through the Linux Docker build environment:**5354```bash55Telegram/build/docker/centos_env/build_debug.sh56```5758**Important:** When running cmake from a shell that doesn't support `cd`, use quoted absolute paths:59```bash60cmake --build "l:\Telegram\tx64\out" --config Debug --target Telegram61```6263**Never build Release** - it's extremely heavy and not needed for testing changes.6465## Platform-Specific Requirements6667### Windows68- Requires Visual Studio 202269- Must run from appropriate Native Tools Command Prompt:70 - "x64 Native Tools Command Prompt" for `win64`71 - "x86 Native Tools Command Prompt" for `win`72 - "ARM64 Native Tools Command Prompt" for `winarm`73- Dependencies: `../win64/Libraries` (64-bit) or `../Libraries` (32-bit)7475### macOS76- Requires Xcode77- Dependencies: `../Libraries/local/Qt-*`78- Set `QT` environment variable: `export QT=6.8`7980### Linux81- Build dependencies in `../Libraries`82- Set `QT` environment variable if needed8384## Key Files8586- **`Telegram/build/version`** - Version information87- **`out/`** - Build output directory8889## Troubleshooting9091### "Libraries not found"92Ensure the repository is in `L:\Telegram\tdesktop`. The build system requires `../win64/Libraries` to exist.9394### Build fails with "wrong command prompt"95On Windows, use the correct Visual Studio Native Tools Command Prompt matching your target (x64/x86/ARM64).9697### macOS crashes while reading the cached language pack9899After an incremental Xcode build that regenerated `lang.strings` outputs, the100app can link a new generated key lookup with stale objects that still use an101older `kKeysCount`. The characteristic failure is:102103- the Debug log stops immediately after104 `Lang Info: Loaded cached, keys: ...`;105- stderr and `tdata/working` may be empty;106- a fresh `~/Library/Logs/DiagnosticReports/Telegram-*.ips` shows `SIGABRT`107 from `std::vector<unsigned char>::operator[]`, then108 `Lang::Instance::applyValue()`, `fillFromSerialized()`, and109 `Local::readLangPack()`.110111If this exact startup failure repeats twice, do not change the implementation,112test overlay, or portable account. Stop only this checkout's exact Telegram113process. Because Xcode's `CONFIGURATION_BUILD_DIR` is `out/Debug`, make a114safety copy of every existing portable folder outside `out/` before cleaning:115116```bash117portable_backup_root="$(mktemp -d "${TMPDIR:-/tmp}/tdesktop-portable-clean.XXXXXX")"118for portable_name in \119 TelegramForcePortable \120 test_TelegramForcePortable \121 real_TelegramForcePortable; do122 if [ -d "out/Debug/$portable_name" ]; then123 ditto "out/Debug/$portable_name" "$portable_backup_root/$portable_name"124 fi125done126```127128Require every expected backup copy to exist before continuing. Then perform129one full Xcode Debug clean and rebuild:130131```bash132cmake --build out --config Debug --target clean133cmake --build out --config Debug --target Telegram134```135136Afterward, restore a portable folder from the backup only when its original137path is missing; never overwrite a folder that survived the clean. Verify all138three original folder names that existed before the clean are present, keep139the backup until the rebuilt app completes one successful launch, and record140its path if the run stops before verification. Then rerun the same test once.141If the signature persists after that clean rebuild, continue normal crash142diagnosis or report the blocker. Do not loop clean rebuilds.143144### Build output locks145146For builds owned by the autonomous `continue` / `perform-task` workflow, read147and follow `.agents/shared/build-lock-recovery.md`. PDB, EXE, OBJ, and other148build-output lock errors are recoverable: stop only the exact checkout149executable or verified build-tree holders, delete only exact named artifacts150inside that checkout's build tree, and retry within the bounded recovery151budget. Never stop an installed Telegram client, another checkout, an IDE, or152an unknown process.153154Outside that autonomous workflow, an exact checkout executable may be running155because the user is testing it. Do not terminate it or delete locked build156outputs without explicit permission. Report the exact locked path and ask the157user to close that checkout's Telegram/debugger before rebuilding.158159## Best Practices1601611. **Always use Debug builds** - Release builds are extremely heavy1622. **Don't build Release configuration** - it's too heavy for testing163164## Text File Format165166- On Windows, keep project text files with CRLF line endings.167- Do not save source, header, build/config, style, or localization files as UTF-8 with BOM. Use UTF-8 without BOM.168- When rewriting project text files for normalization, preserve file content otherwise and do not introduce a BOM.169170## Commits171172- Subject: one concise, plain-language line summarizing the change, ~50-60 characters, matching the style of recent `git log` subjects. This is usually the entire message.173- For an `ai-tdesktop` task, start the subject with exactly `[ai] ` when the174 retained task implementation changes permanent test-helper code, the agent175 harness, or agent documentation in any way. This includes176 `Telegram/SourceFiles/test/`, `.agents/`, `.claude/`, `AGENTS.md`,177 `CLAUDE.md`, and files whose sole role is supporting those systems. Do not178 count the disposable test overlay or external AI task artifacts. For every179 other task, the subject must not contain `[ai]` anywhere.180- For ordinary work not associated with an AI task, add a short plain-language body only when the subject can't carry it (what was done, not the technical how) — a line or two at most.181- Never add a `Co-Authored-By:` line or any tool/assistant attribution trailer.182- Never add `Autotask:`/attempt or other internal run markers. A commit owned by183 an `ai-tdesktop` task has exactly three lines: the concise subject, a blank184 line, and `Task: <task-id>`. Do not add a body. Keep rationale and185 implementation notes out of the commit message; put a short durable note186 under `tasks/<task-id>.md` only when useful. Do not copy commit hashes into187 that note or any AI task artifact; the task id is the cross-repository link.188189## Local Storage Serialization190191Both app-level (`Core::Settings`) and session-level (`Main::SessionSettings`) use sequential binary serialization via `QDataStream`. Key rules:192193- New fields must ALWAYS be appended at the **end** of the stream, never inserted in the middle194- Reading new fields must be guarded with `!stream.atEnd()` and provide a meaningful default/fallback195- Inserting in the middle breaks reading of data saved by older versions (the new read code consumes bytes that belong to subsequent fields)196- For simple flags and values, prefer using the generic KV prefs facility (`writePref<Type>` / `readPref<Type>`) instead of adding to the binary stream -- this avoids serialization ordering issues entirely197198---199200# Development Guidelines201202## Coding Style203204**Do NOT write comments in code:**205206This is important! Do not write single-line comments that describe what the next line does - they are bloat. Comments are allowed ONLY to describe complex algorithms in detail, when the explanation requires at least 4-5 lines. Self-documenting code with clear variable and function names is preferred.207208```cpp209// BAD - don't do this:210// Get the user's name211auto name = user->name();212// Check if premium213if (user->isPremium()) {214215// GOOD - no comments needed, code is self-explanatory:216auto name = user->name();217if (user->isPremium()) {218219// ACCEPTABLE - complex algorithm explanation (4+ lines):220// The algorithm works by first collecting all visible messages221// in the viewport, then calculating their intersection with222// the clip rectangle. Messages are grouped by date headers,223// and we need to account for sticky headers that may overlap224// with the first message in each group.225```226227**Style and formatting rules** are in `REVIEW.md` — see that file for empty-line-before-closing-brace, operator placement in multi-line expressions, if-with-initializer, and other mechanical style rules.228229**Use `auto` for type deduction:**230231Prefer `auto` (or `const auto`, `const auto &`) instead of explicit types:232233```cpp234// Prefer this:235auto currentTitle = tr::lng_settings_title(tr::now);236auto nameProducer = GetNameProducer();237238// Instead of this:239QString currentTitle = tr::lng_settings_title(tr::now);240rpl::producer<QString> nameProducer = GetNameProducer();241```242243**Use trailing return types only when the normal form is too long:**244245Prefer the normal return type form when the opening line fits comfortably, roughly around 77 characters or less:246247```cpp248// GOOD:249[[nodiscard]] TextWithEntities FlattenSummaryBlocks(250 const std::vector<Block> &blocks);251```252253Do not use one-line trailing return types, or put the trailing return type after `)` on the same line. If it fits on one line with trailing syntax, the normal form would be shorter and easier to read:254255```cpp256// BAD:257auto ComputeTitle() -> QString;258259// BAD:260[[nodiscard]] auto FlattenSummaryBlocks(261 const std::vector<Block> &blocks) -> TextWithEntities;262```263264Use `auto` with a trailing return type only when the normal opening line265`{attributes} {return-type} {class-name::}{function-name(}` would be too long, or would force the return type onto its own line. Put the arrow and return type on the next line so the return type remains easy to find:266267```cpp268// BAD:269not_null<HistoryView::Controls::ComposeAiButton*>270HistoryView::Controls::SetupCaptionAiButton(SetupCaptionAiButtonArgs &&args);271```272273```cpp274// GOOD:275auto HistoryView::Controls::SetupCaptionAiButton(276 SetupCaptionAiButtonArgs &&args)277-> not_null<HistoryView::Controls::ComposeAiButton*>;278```279280This applies to both declarations and definitions.281282**Use `_q` for QString literals:**283284Prefer the project literal `u"..."_q` instead of the verbose `QStringLiteral("...")` macro when creating `QString` values:285286```cpp287// Prefer this:288auto text = u"Settings"_q;289290// Instead of this:291auto text = QStringLiteral("Settings");292```293294**Never use `Q_OS_LINUX` for platform checks in new code:**295296Telegram Desktop distinguishes at most three platforms: Windows / macOS / all-other. The "all-other" branch covers Linux, the BSD variants and more — and this is almost always the branch you want. `Q_OS_LINUX` narrows it to Linux alone, silently excluding the non-Linux Unix platforms, which is almost never intended. For the all-other branch use `!defined Q_OS_WIN && !defined Q_OS_MAC` at compile time, or its runtime equivalent `Platform::IsLinux()` — which, despite the name, means exactly `!defined Q_OS_WIN && !defined Q_OS_MAC` ("everything except Windows and macOS"), not Linux specifically:297298```cpp299// BAD - excludes FreeBSD and other non-Linux Unix:300#ifdef Q_OS_LINUX301UnixSpecificCode();302#endif // Q_OS_LINUX303304// GOOD - the all-other branch, compile time:305#if !defined Q_OS_WIN && !defined Q_OS_MAC306UnixSpecificCode();307#endif // !Q_OS_WIN && !Q_OS_MAC308309// GOOD - the all-other branch, runtime (same meaning, NOT Linux-only):310if (Platform::IsLinux()) {311 UnixSpecificCode();312}313```314315`Q_OS_LINUX` is only for the rare case where you genuinely want exactly Linux and not the other Unix-like systems — usually you don't. The few existing uses (`Telegram/SourceFiles/core/sandbox.cpp`, `Telegram/SourceFiles/platform/linux/specific_linux.cpp`) are such genuinely Linux-only code paths and stay as-is.316317**Treat CMake `LINUX` as the all-other platform:**318319In this project, `cmake/validate_special_target.cmake` sets `LINUX` in the320final `else()` after checking `WIN32` and `APPLE`. It therefore means321`NOT WIN32 AND NOT APPLE`, including non-Linux Unix platforms; it does not322mean exactly Linux. For the usual three-way platform split, write:323324```cmake325if (WIN32)326 set(platform_source platform/win.cpp)327elseif (APPLE)328 set(platform_source platform/mac.mm)329else()330 set(platform_source platform/linux.cpp)331endif()332target_sources(my_target PRIVATE ${platform_source})333```334335Do not add a separate fallback branch after `if (LINUX)` as though `LINUX`336were one platform among several remaining platforms. There are no remaining337platforms in this project's CMake platform model.338339**Prefer cppgir wrappers over the GLib C API:**340341When implementing all-other-platform code with GLib, GObject, or GIO, use the342generated cppgir C++ bindings under `gi::repository` as much as possible.343Prefer their `GLib`, `GObject`, and `Gio` types, ownership handling, results,344and callbacks over raw `g_*`, `g_object_*`, and `g_io_*` APIs. Use the C API345only when cppgir does not expose the required functionality or at a narrow346interop boundary that genuinely requires raw GLib types, and keep that raw347API surface as small as possible.348349**Generate typed D-Bus bindings from introspection XML:**350351For a D-Bus interface known at build time, prefer the CMake `generate_dbus`352function from `cmake/external/glib/generate_dbus.cmake` over handwritten353`GDBusProxy` calls, stringly typed method and signal names, or manually354maintained C wrappers. Its signature is:355356```cmake357generate_dbus(358 target_name359 interface_prefix360 namespace361 interface_file)362```363364`target_name` is the existing target that will use the bindings,365`interface_prefix` is the common D-Bus interface prefix passed to366`gdbus-codegen`, `namespace` names the generated API, and `interface_file` is367the D-Bus introspection XML file. Include the helper and call it inside the368all-other-platform branch:369370```cmake371include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake)372generate_dbus(373 my_target374 org.example.375 Example376 ${src_loc}/platform/linux/org.example.Service.xml)377```378379The helper runs `gdbus-codegen`, generates proxy, skeleton, and object-manager380types, produces GIR metadata, wraps that metadata with cppgir, and links the381result into `target_name`. Consume the resulting typed API from382`gi::repository::Example` (using the namespace argument from the example);383do not edit or separately list files under the build `gen` directory. Use384generic GLib D-Bus calls only when the interface is genuinely dynamic or385cannot be represented by suitable introspection XML.386387## API Usage388389### API Schema Files390391API definitions use [TL Language](https://core.telegram.org/mtproto/TL):3923931. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`** - MTProto protocol (encryption, auth, etc.)3942. **`Telegram/SourceFiles/mtproto/scheme/api.tl`** - Telegram API (messages, users, chats, etc.)395396### Making API Requests397398Standard pattern using `api()`, generated `MTP...` types, and callbacks:399400```cpp401api().request(MTPnamespace_MethodName(402 MTP_flags(flags_value),403 MTP_inputPeer(peer),404 MTP_string(messageText),405 MTP_long(randomId),406 MTP_vector<MTPMessageEntity>()407)).done([=](const MTPResponseType &result) {408 // Handle successful response409410 // Multiple constructors - use .match() or check type:411 result.match([&](const MTPDuser &data) {412 // use data.vfirst_name().v413 }, [&](const MTPDuserEmpty &data) {414 // handle empty user415 });416417 // Single constructor - use .data() shortcut:418 const auto &data = result.data();419 // use data.vmessages().v420421}).fail([=](const MTP::Error &error) {422 // Handle API error423 if (error.type() == u"FLOOD_WAIT_X"_q) {424 // Handle flood wait425 }426}).handleFloodErrors().send();427```428429**Key points:**430- Always refer to `api.tl` for method signatures and return types431- Use generated `MTP...` types for parameters (`MTP_int`, `MTP_string`, etc.)432- For multiple constructors, use `.match()` or check `.type()` against `mtpc_` constants then call `.c_constructorName()`:433```cpp434 // Using match:435 result.match([&](const MTPDuser &data) { ... }, [&](const MTPDuserEmpty &data) { ... });436 // Or explicit type check:437 if (result.type() == mtpc_user) {438 const auto &data = result.c_user(); // asserts on type mismatch439 }440```441- For single constructors, use `.data()` shortcut442- Include `.handleFloodErrors()` before `.send()` in rare cases where you want special case flood error handling443- Silently ignore HTTP 406 errors in UI: the server uses 406 to mean "show nothing to the user". Guard toasts with `MTP::IgnoreError(error)` or use `MTP::ShowErrorFallback(show, error)` (both in `mtproto/mtproto_response.h`) which shows `error.type()` as a toast unless the error should be ignored.444445### API Request Callback Lifetime446447`api().request(...)` callbacks are owned by the session, not by whatever created448them. A `.done()` / `.fail()` handler stays alive for the whole session lifetime,449so a handler that captured a widget, a box, a controller, or any shorter-lived450state still runs after that state is gone. A plain `[=]` capture warns about451nothing, which makes this one of the easiest ways to write a use-after-free here.452453Capturing only plain values or session-owned objects is fine. When anything454captured can die before the session does, pick one of three:455456**1. Guard the callback with `crl::guard`.** The request is always sent; the457handler is skipped when the context is gone. Use when the call itself must reach458the server and only the local reaction is optional.459460```cpp461api().request(MTPmethod(462 ...463)).done(crl::guard(this, [=](const MTPResult &result) {464 // runs only while `this` is still alive465})).send();466```467468Accepted guards, in rough order of how often they are used: a raw pointer or469`not_null` to any `QObject`-derived type — widgets, boxes, controllers — where the470`QPointer` is created on the spot, so passing `this` is the normal case; a raw471pointer or `not_null` to a `base::has_weak_ptr` type; `QPointer`, `QWeakPointer`,472`QSharedPointer`; `base::weak_ptr`, `base::weak_qptr`; `std::weak_ptr`,473`std::shared_ptr`; and `base::binary_guard`.474475**2. Remember the `mtpRequestId` and cancel it.** Cancel when the result stops476being relevant, and in the destructor. The request may never reach the server —477if it is still queued when cancelled, or connectivity dies first, it is simply478dropped — so never use this when the call itself has to happen.479480```cpp481_requestId = api().request(MTPmethod(482 ...483)).done([=](const MTPResult &result) {484 _requestId = 0;485 ...486}).send();487488// when the result is no longer relevant, and in the destructor:489api().request(base::take(_requestId)).cancel();490```491492**3. Own an `MTP::Sender`.** Its destructor cancels everything it sent that is493still in flight, so request lifetime follows the owner with no bookkeeping. Same494delivery caveat as (2). Prefer this for a widget, box, or controller that issues495more than a request or two.496497```cpp498// header499 MTP::Sender _api;500501// constructor initializer list502, _api(&session->mtp())503504// requests sent through it die with the owner505_api.request(MTPmethod(506 ...507)).done([=](const MTPResult &result) {508 ...509}).send();510```511512Choosing between them: if the server must see the request, use (1). If it only513matters while its owner is alive, use (3) — or (2) when a single request does not514justify a `Sender` member.515516## UI Styling517518### Style Files519520UI styles are defined in `.style` files using custom syntax:521522```style523using "ui/basic.style";524using "ui/widgets/widgets.style";525526MyButtonStyle {527 textPadding: margins;528 icon: icon;529 height: pixels;530}531532defaultButton: MyButtonStyle {533 textPadding: margins(10px, 15px, 10px, 15px);534 icon: icon{{ "gui/icons/search", iconColor }};535 height: 30px;536}537538primaryButton: MyButtonStyle(defaultButton) {539 icon: icon{{ "gui/icons/check", iconColor }};540}541```542543**Built-in types:**544- `int` - Integer numbers (e.g., `maxLines: 3;`)545- `bool` - Boolean values (e.g., `useShadow: true;`)546- `pixels` - Pixel values with `px` suffix (e.g., `10px`)547- `color` - Named colors from `ui/colors.palette`548- `icon` - Inline icon definition: `icon{{ "path/stem", color }}`549- `margins` - Four values: `margins(left, top, right, bottom)`550- `size` - Two values: `size(width, height)`551- `point` - Two values: `point(x, y)`552- `align` - Alignment: `align(center)`, `align(left)`553- `font` - Font: `font(14px semibold)`554- `double` - Floating point555556**Multi-part icons** (layers drawn bottom-up):557```style558myComplexIcon: icon{559 { "gui/icons/background", iconBgColor },560 { "gui/icons/foreground", iconFgColor }561};562```563564**Borders** are typically separate fields, not a single property:565```style566chatInput {567 border: 1px; // width568 borderFg: defaultInputFieldBorder; // color569}570```571572**Never hardcode sizes in code:**573574The app supports different interface scale options. Style `px` values are automatically scaled at runtime, but raw integer constants in code are not. Never use hardcoded numbers for margins, paddings, spacing, sizes, coordinates, or any other dimensional values. Always define them in `.style` files and reference via `st::`.575576```cpp577// BAD - breaks at non-100% interface scale:578p.drawText(10, 20, text);579widget->setFixedHeight(48);580auto margin = 8;581auto iconSize = QSize(24, 24);582583// GOOD - define in .style file and reference:584p.drawText(st::myWidgetTextLeft, st::myWidgetTextTop, text);585widget->setFixedHeight(st::myWidgetHeight);586auto margin = st::myWidgetMargin;587auto iconSize = st::myWidgetIconSize;588```589590**Duration constants**: Animation durations should NOT go in `.style` files, this is a legacy approach. Prefer `constexpr auto kName = crl::time(N)` in an anonymous namespace in the relevant `.cpp` file.591592### Usage in Code593594```cpp595#include "styles/style_widgets.h"596597// Access style members598int height = st::primaryButton.height;599const style::icon &icon = st::primaryButton.icon;600style::margins padding = st::primaryButton.textPadding;601602// Use in painting603void MyWidget::paintEvent(QPaintEvent *e) {604 Painter p(this);605 p.fillRect(rect(), st::chatInput.backgroundColor);606}607```608609## Localization610611### String Definitions612613Strings are defined in `Telegram/Resources/langs/lang.strings`:614615```616"lng_settings_title" = "Settings";617"lng_confirm_delete_item" = "Are you sure you want to delete {item_name}?";618"lng_files_selected#one" = "{count} file selected";619"lng_files_selected#other" = "{count} files selected";620```621622### Usage in Code623624**Immediate (current value):**625626```cpp627auto currentTitle = tr::lng_settings_title(tr::now);628629auto currentConfirmation = tr::lng_confirm_delete_item(630 tr::now,631 lt_item_name, currentItemName);632633auto filesText = tr::lng_files_selected(tr::now, lt_count, count);634```635636**Reactive (rpl::producer):**637638```cpp639auto titleProducer = tr::lng_settings_title();640641auto confirmationProducer = tr::lng_confirm_delete_item(642 lt_item_name,643 std::move(itemNameProducer));644645auto filesTextProducer = tr::lng_files_selected(646 lt_count,647 countProducer | tr::to_count());648```649650**Key points:**651- Pass `tr::now` as first argument for immediate `QString`652- Omit `tr::now` for reactive `rpl::producer<QString>`653- Placeholders use `lt_tag_name, value` pattern654- For `{count}`: immediate uses `int`, reactive uses `rpl::producer<float64>` with `| tr::to_count()`655- Move producers with `std::move` when passing to placeholders656- Rich text projectors — these `tr::` helpers serve double duty: as the **last argument** (projector) they set the return type to `TextWithEntities`, and as **placeholder values** they wrap individual substitutions in formatting. Always prefer them over `Ui::Text::Bold()`, `Ui::Text::RichLangValue`, etc. — see REVIEW.md for the full mapping.657 - `tr::marked` — basic projection, converts `QString` to `TextWithEntities`658 - `tr::rich` — interprets `**bold**`/`__italic__` markup in the string659 - `tr::bold`, `tr::italic`, `tr::underline` — wrap text in that formatting660 - `tr::link` — wrap as a clickable link661 - `tr::url(u"https://..."_q)` — returns a projection that converts text to a link pointing to the given URL; can be passed to `rpl::map` or directly to a `tr::lng_...` call662```cpp663 // As last argument (projector):664 auto title = tr::lng_export_progress_title(tr::now, tr::bold);665 auto text = tr::lng_proxy_incorrect_secret(tr::now, tr::rich);666 // As placeholder value wrapper + projector:667 auto desc = tr::lng_some_key(668 tr::now,669 lt_name,670 tr::bold(userName),671 lt_group,672 tr::bold(groupName),673 tr::rich);674 // Nested tr::lng as placeholder:675 auto linked = tr::lng_settings_birthday_contacts(676 lt_link,677 tr::lng_settings_birthday_contacts_link(tr::url(link)),678 tr::marked);679```680681## RPL (Reactive Programming Library)682683### Core Concepts684685**Producers** represent streams of values over time:686687```cpp688auto intProducer = rpl::single(123); // Emits single value689auto lifetime = rpl::lifetime(); // Manages subscription lifetime690```691692### Starting Pipelines693694```cpp695std::move(counter) | rpl::on_next([=](int value) {696 qDebug() << "Received: " << value;697}, lifetime);698699// Without lifetime parameter - MUST store returned lifetime:700auto subscriptionLifetime = std::move(counter) | rpl::on_next([=](int value) {701 // process value702});703```704705### Transforming Producers706707```cpp708auto strings = std::move(ints) | rpl::map([](int value) {709 return QString::number(value * 2);710});711712auto evenInts = std::move(ints) | rpl::filter([](int value) {713 return (value % 2 == 0);714});715```716717### Combining Producers718719**`rpl::combine`** - combines latest values (lambdas receive unpacked arguments):720721```cpp722auto combined = rpl::combine(countProducer, textProducer);723724std::move(combined) | rpl::on_next([=](int count, const QString &text) {725 qDebug() << "Count=" << count << ", Text=" << text;726}, lifetime);727```728729**`rpl::merge`** - merges producers of same type:730731```cpp732auto merged = rpl::merge(sourceA, sourceB);733734std::move(merged) | rpl::on_next([=](QString &&value) {735 qDebug() << "Merged value: " << value;736}, lifetime);737```738739**Other pipeline starters** — besides `rpl::on_next`, there are:740- `rpl::on_error([=](Error &&e) { ... }, lifetime)` — handle errors741- `rpl::on_done([=] { ... }, lifetime)` — handle stream completion742- `rpl::on_next_error_done(nextCb, errorCb, doneCb, lifetime)` — handle all three743744The `Error` template parameter defaults to `rpl::no_error`: `rpl::producer<Type, Error = no_error>`.745746**Key points:**747- Explicitly `std::move` producers when starting pipelines748- Pass `rpl::lifetime` to `on_...` methods or store returned lifetime749- Use `rpl::duplicate(producer)` to reuse a producer multiple times750- Combined producers automatically unpack tuples in lambdas (works with `rpl::map`, `rpl::filter`, and `rpl::on_next`)751
Same format, overlapping stack, ranked by quality.
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| vllm-project/vllmAGENTS.md · 89k | AGENTS.md | setuptestlint-formatstyle+5 | 100/100 | 14 days ago | |
| duckduckgo/content-scope-scriptsspecial-pages/AGENTS.md · 70 | AGENTS.md | buildteststylearch+3 | 100/100 | 14 days ago | |
| TryGhost/Ghoste2e/AGENTS.md · 55k | AGENTS.md | setupteststylearch+2 | 100/100 | today | |
| deepseek-ai/deepseek-harnessnative/landlock-run/AGENTS.md · 104k | AGENTS.md | setupteststylearch+3 | 100/100 | today | |
| aaif-goose/gooseAGENTS.md · 53k | AGENTS.md | setupbuildtestlint-format+7 | 100/100 | 8 days ago | |
| n8n-io/n8npackages/@n8n/agents/AGENTS.md · 201k | AGENTS.md | buildteststylearch+3 | 100/100 | 14 days ago | |
| code-yeongyu/oh-my-openagentpackages/web/AGENTS.md · 68k | AGENTS.md | setupbuildtestlint-format+6 | 100/100 | 13 days ago | |
| mui/material-uiAGENTS.md · 99k | AGENTS.md | setupbuildtestlint-format+9 | 100/100 | 14 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/telegramdesktop-tdesktop-agents)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.