diff --git a/.codex/skills/task-think/PROMPTS.md b/.codex/skills/task-think/PROMPTS.md new file mode 100644 index 0000000000..825e334ba5 --- /dev/null +++ b/.codex/skills/task-think/PROMPTS.md @@ -0,0 +1,111 @@ +# Phase Prompts + +Use these templates for `codex exec --json` child runs. Replace ``, ``, and ``. + +## Phase 1: Context + +```text +You are the context phase for task "" in repository . + +Read CLAUDE.md for the basic coding rules and guidelines. + +Read AGENTS.md and all relevant source files. Write a focused context doc: +- .ai//context.md + +Include: +1. Relevant files and why they matter +2. Existing patterns to follow +3. Risks and unknowns +4. Verification hooks (what to build/test later) + +Do not implement code in this phase. +``` + +## Phase 2: Plan + +```text +You are the planning phase for task "" in repository . + +Read CLAUDE.md for the basic coding rules and guidelines. + +Read: +- .ai//inputs.md +- .ai//context.md + +Create: +- .ai//plan.md + +Plan requirements: +1. Concrete file-level edits +2. Ordered phases +3. Verification commands +4. Rollback/risk notes +``` + +## Phase 3: Implement + +```text +You are the implementation phase for task "" in repository . + +Read CLAUDE.md for the basic coding rules and guidelines. + +Read: +- .ai//inputs.md +- .ai//context.md +- .ai//plan.md + +Implement the plan in code. Then write: +- .ai//implementation.md + +Include: +1. Files changed +2. What was implemented +3. Any deviations from plan and why +``` + +## Phase 4: Verify + +```text +You are the verification phase for task "" in repository . + +Read CLAUDE.md for the basic coding rules and guidelines. + +Read: +- .ai//plan.md +- .ai//implementation.md + +Run the relevant build/test commands from AGENTS.md and plan.md. +Append results to: +- .ai//implementation.md + +If blocked by locked files or access errors, stop and report exact blocker. +``` + +## Phase 5: Review + +```text +You are the review phase for task "" in repository . + +Read CLAUDE.md for the basic coding rules and guidelines. + +Read: +- .ai//context.md +- .ai//plan.md +- .ai//implementation.md + +Perform a code review focused on regressions, thread-safety, performance, and missing tests. +Write: +- .ai//review.md + +If issues are found, implement fixes and update implementation.md/review.md with final status. +``` + +## Example Runner Commands + +```powershell +codex exec --json -C "" | Tee-Object .ai//logs/phase-1-context.jsonl +codex exec --json -C "" | Tee-Object .ai//logs/phase-2-plan.jsonl +codex exec --json -C "" | Tee-Object .ai//logs/phase-3-implement.jsonl +codex exec --json -C "" | Tee-Object .ai//logs/phase-4-verify.jsonl +codex exec --json -C "" | Tee-Object .ai//logs/phase-5-review.jsonl +``` diff --git a/.codex/skills/task-think/SKILL.md b/.codex/skills/task-think/SKILL.md new file mode 100644 index 0000000000..b026e02899 --- /dev/null +++ b/.codex/skills/task-think/SKILL.md @@ -0,0 +1,66 @@ +--- +name: task-think +description: Orchestrate a multi-phase implementation workflow for this repository with artifact files under .ai/task-slug and optional fresh codex exec child runs per phase. Use when the user wants one prompt to drive context gathering, planning, implementation, verification, and review iterations while keeping the main session context clean. +--- + +# Task Pipeline + +Run a full implementation workflow with repository artifacts and clear phase boundaries. + +## Inputs + +Collect: +- task description +- optional task slug (if missing, derive a short kebab-case name) +- optional constraints (files, architecture, deadlines, risk tolerance) +- optional screenshot paths + +If screenshots are attached in UI but not present as files, write a brief textual summary in `.ai//inputs.md` so child runs can consume the requirements. + +## Artifacts + +Create and maintain: +- `.ai//inputs.md` +- `.ai//context.md` +- `.ai//plan.md` +- `.ai//implementation.md` +- `.ai//review.md` +- `.ai//logs/phase-*.jsonl` (when running child `codex exec`) + +## Execution Mode + +Run `codex exec --json` child sessions for each phase. + +## Fresh-Run Mode Procedure + +1. Confirm repository root and task slug. +2. Create `.ai//` and `logs/`. +3. Run child phase sessions sequentially, waiting for each to finish. +4. After each phase, validate artifact file exists and has substantive content. +5. Summarize status in the parent session after each phase. +6. Stop immediately on blocking errors and report exact blocker. + +Use the phase prompt templates in `PROMPTS.md`. + +## Verification Rules + +- If build or test commands fail due to file locks or access-denied outputs, stop and ask the user to close locking processes before retrying. +- Never claim completion without: + - implemented code changes present + - build/test attempt results recorded + - review pass documented with any follow-up fixes + +## Completion Criteria + +Mark complete only when: +- plan phases are done +- verification results are recorded +- review issues are addressed or explicitly deferred with rationale + +## User Invocation + +Use plain language with the skill name in the request, for example: + +`Use local task-think skill: make sure FileLoadTask::process does not create or read QPixmap on background threads; use QImage with ARGB32_Premultiplied instead.` + +If screenshots are relevant, include file paths in the same prompt. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..df29ec3c61 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,429 @@ +# Agent Guide for Telegram Desktop + +This guide defines repository-wide instructions for coding agents working with the Telegram Desktop codebase. + +## Build System Structure + +The build system expects this directory layout: + +```text +L:\Telegram\ # BuildPath +L:\Telegram\tdesktop\ # Repository (you work here) +L:\Telegram\Libraries\ # 32-bit dependencies (Linux/macOS) +L:\Telegram\win64\Libraries\ # 64-bit dependencies (Windows) +L:\Telegram\ThirdParty\ # Build tools (NuGet, Python, etc.) +``` + +Dependencies are located relative to the repository: `../Libraries`, `../win64/Libraries`, or `../ThirdParty`. + +## Build Configuration + +### Build Commands + +**From repository root, run:** + +```bash +cmake --build out --config Debug --target Telegram +``` + +That's it. The `out/` directory is already configured. The executable will be at `out/Debug/Telegram.exe`. + +**Important:** When running cmake from a shell that doesn't support `cd`, use quoted absolute paths: +```bash +cmake --build "l:\Telegram\tx64\out" --config Debug --target Telegram +``` + +**Never build Release** - it's extremely heavy and not needed for testing changes. + +## Platform-Specific Requirements + +### Windows +- Requires Visual Studio 2022 +- Must run from appropriate Native Tools Command Prompt: + - "x64 Native Tools Command Prompt" for `win64` + - "x86 Native Tools Command Prompt" for `win` + - "ARM64 Native Tools Command Prompt" for `winarm` +- Dependencies: `../win64/Libraries` (64-bit) or `../Libraries` (32-bit) + +### macOS +- Requires Xcode +- Dependencies: `../Libraries/local/Qt-*` +- Set `QT` environment variable: `export QT=6.8` + +### Linux +- Build dependencies in `../Libraries` +- Set `QT` environment variable if needed + +## Key Files + +- **`Telegram/build/version`** - Version information +- **`out/`** - Build output directory + +## Troubleshooting + +### "Libraries not found" +Ensure the repository is in `L:\Telegram\tdesktop`. The build system requires `../win64/Libraries` to exist. + +### Build fails with "wrong command prompt" +On Windows, use the correct Visual Studio Native Tools Command Prompt matching your target (x64/x86/ARM64). + +### Build fails with PDB or EXE access errors + +**⚠️ CRITICAL: DO NOT RETRY THE BUILD. STOP AND WAIT FOR USER.** + +If the build fails with ANY of these errors: +- `fatal error C1041: cannot open program database` +- `cannot open output file 'Telegram.exe'` +- `LNK1104: cannot open file` +- Any "access denied" or "file in use" error + +**STOP IMMEDIATELY.** These errors mean files are locked by a running process (Telegram.exe or debugger). + +**What to do:** +1. Do NOT attempt another build - it will fail the same way +2. Do NOT try to delete files - they are locked +3. Do NOT try any workarounds or fixes +4. IMMEDIATELY inform the user: + +> "Build failed - files are locked. Please close Telegram.exe (and any debugger) so I can rebuild." + +**Then WAIT for user confirmation before attempting any build.** + +Retrying builds wastes time and context. The ONLY fix is for the user to close the running process. + +## Best Practices + +1. **Always use Debug builds** - Release builds are extremely heavy +2. **Don't build Release configuration** - it's too heavy for testing + +--- + +# Development Guidelines + +## Coding Style + +**Do NOT write comments in code:** + +This 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. + +```cpp +// BAD - don't do this: +// Get the user's name +auto name = user->name(); +// Check if premium +if (user->isPremium()) { + +// GOOD - no comments needed, code is self-explanatory: +auto name = user->name(); +if (user->isPremium()) { + +// ACCEPTABLE - complex algorithm explanation (4+ lines): +// The algorithm works by first collecting all visible messages +// in the viewport, then calculating their intersection with +// the clip rectangle. Messages are grouped by date headers, +// and we need to account for sticky headers that may overlap +// with the first message in each group. +``` + +**Empty line before closing brace:** + +Always add an empty line before the closing brace of a class (after all private fields): + +```cpp +// GOOD: +class MyClass { +public: + void foo(); + +private: + int _value = 0; + +}; + +// BAD: +class MyClass { +public: + void foo(); + +private: + int _value = 0; +}; +``` + +**Multi-line expressions — operators at the start of continuation lines:** + +When splitting an expression across multiple lines, place operators (like `&&`, `||`, `;`, `+`, etc.) at the **beginning** of continuation lines, not at the end of the previous line. This makes it immediately obvious from the left edge whether a line is a continuation or new code. + +```cpp +// BAD - continuation looks like scope code: +if (const auto &lottie = animation->lottie; + lottie && lottie->valid() && lottie->framesCount() > 1) { + lottie->animate([=] { + +// GOOD - semicolon at start signals continuation: +if (const auto &lottie = animation->lottie + ; lottie && lottie->valid() && lottie->framesCount() > 1) { + lottie->animate([=] { + +// BAD - trailing && makes next line look like independent code: +if (veryLongExpression() && + anotherLongExpression() && + anotherOne()) { + doSomething(); + +// GOOD - leading && clearly marks continuation: +if (veryLongExpression() + && anotherLongExpression() + && anotherOne()) { + doSomething(); +``` + +**Use `auto` for type deduction:** + +Prefer `auto` (or `const auto`, `const auto &`) instead of explicit types: + +```cpp +// Prefer this: +auto currentTitle = tr::lng_settings_title(tr::now); +auto nameProducer = GetNameProducer(); + +// Instead of this: +QString currentTitle = tr::lng_settings_title(tr::now); +rpl::producer nameProducer = GetNameProducer(); +``` + +## API Usage + +### API Schema Files + +API definitions use [TL Language](https://core.telegram.org/mtproto/TL): + +1. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`** - MTProto protocol (encryption, auth, etc.) +2. **`Telegram/SourceFiles/mtproto/scheme/api.tl`** - Telegram API (messages, users, chats, etc.) + +### Making API Requests + +Standard pattern using `api()`, generated `MTP...` types, and callbacks: + +```cpp +api().request(MTPnamespace_MethodName( + MTP_flags(flags_value), + MTP_inputPeer(peer), + MTP_string(messageText), + MTP_long(randomId), + MTP_vector() +)).done([=](const MTPResponseType &result) { + // Handle successful response + + // Multiple constructors - use .match() or check type: + result.match([&](const MTPDuser &data) { + // use data.vfirst_name().v + }, [&](const MTPDuserEmpty &data) { + // handle empty user + }); + + // Single constructor - use .data() shortcut: + const auto &data = result.data(); + // use data.vmessages().v + +}).fail([=](const MTP::Error &error) { + // Handle API error + if (error.type() == u"FLOOD_WAIT_X"_q) { + // Handle flood wait + } +}).handleFloodErrors().send(); +``` + +**Key points:** +- Always refer to `api.tl` for method signatures and return types +- Use generated `MTP...` types for parameters (`MTP_int`, `MTP_string`, etc.) +- For multiple constructors, use `.match()` or check `.type()` then `.c_constructor()` +- For single constructors, use `.data()` shortcut +- Include `.handleFloodErrors()` before `.send()` in rare cases where you want special case flood error handling + +## UI Styling + +### Style Files + +UI styles are defined in `.style` files using custom syntax: + +```style +using "ui/basic.style"; +using "ui/widgets/widgets.style"; + +MyButtonStyle { + textPadding: margins; + icon: icon; + height: pixels; +} + +defaultButton: MyButtonStyle { + textPadding: margins(10px, 15px, 10px, 15px); + icon: icon{{ "gui/icons/search", iconColor }}; + height: 30px; +} + +primaryButton: MyButtonStyle(defaultButton) { + icon: icon{{ "gui/icons/check", iconColor }}; +} +``` + +**Built-in types:** +- `int` - Integer numbers +- `pixels` - Pixel values with `px` suffix (e.g., `10px`) +- `color` - Named colors from `ui/colors.palette` +- `icon` - Inline icon definition: `icon{{ "path/stem", color }}` +- `margins` - Four values: `margins(top, right, bottom, left)` +- `size` - Two values: `size(width, height)` +- `point` - Two values: `point(x, y)` +- `align` - Alignment: `align(center)`, `align(left)` +- `font` - Font: `font(14px semibold)` +- `double` - Floating point + +**Never hardcode sizes in code:** + +The 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::`. + +```cpp +// BAD - breaks at non-100% interface scale: +p.drawText(10, 20, text); +widget->setFixedHeight(48); +auto margin = 8; +auto iconSize = QSize(24, 24); + +// GOOD - define in .style file and reference: +p.drawText(st::myWidgetTextLeft, st::myWidgetTextTop, text); +widget->setFixedHeight(st::myWidgetHeight); +auto margin = st::myWidgetMargin; +auto iconSize = st::myWidgetIconSize; +``` + +### Usage in Code + +```cpp +#include "styles/style_widgets.h" + +// Access style members +int height = st::primaryButton.height; +const style::icon &icon = st::primaryButton.icon; +style::margins padding = st::primaryButton.textPadding; + +// Use in painting +void MyWidget::paintEvent(QPaintEvent *e) { + Painter p(this); + p.fillRect(rect(), st::chatInput.backgroundColor); +} +``` + +## Localization + +### String Definitions + +Strings are defined in `Telegram/Resources/langs/lang.strings`: + +``` +"lng_settings_title" = "Settings"; +"lng_confirm_delete_item" = "Are you sure you want to delete {item_name}?"; +"lng_files_selected#one" = "{count} file selected"; +"lng_files_selected#other" = "{count} files selected"; +``` + +### Usage in Code + +**Immediate (current value):** + +```cpp +auto currentTitle = tr::lng_settings_title(tr::now); + +auto currentConfirmation = tr::lng_confirm_delete_item( + tr::now, + lt_item_name, currentItemName); + +auto filesText = tr::lng_files_selected(tr::now, lt_count, count); +``` + +**Reactive (rpl::producer):** + +```cpp +auto titleProducer = tr::lng_settings_title(); + +auto confirmationProducer = tr::lng_confirm_delete_item( + lt_item_name, + std::move(itemNameProducer)); + +auto filesTextProducer = tr::lng_files_selected( + lt_count, + countProducer | tr::to_count()); +``` + +**Key points:** +- Pass `tr::now` as first argument for immediate `QString` +- Omit `tr::now` for reactive `rpl::producer` +- Placeholders use `lt_tag_name, value` pattern +- For `{count}`: immediate uses `int`, reactive uses `rpl::producer` with `| tr::to_count()` +- Move producers with `std::move` when passing to placeholders + +## RPL (Reactive Programming Library) + +### Core Concepts + +**Producers** represent streams of values over time: + +```cpp +auto intProducer = rpl::single(123); // Emits single value +auto lifetime = rpl::lifetime(); // Manages subscription lifetime +``` + +### Starting Pipelines + +```cpp +std::move(counter) | rpl::on_next([=](int value) { + qDebug() << "Received: " << value; +}, lifetime); + +// Without lifetime parameter - MUST store returned lifetime: +auto subscriptionLifetime = std::move(counter) | rpl::on_next([=](int value) { + // process value +}); +``` + +### Transforming Producers + +```cpp +auto strings = std::move(ints) | rpl::map([](int value) { + return QString::number(value * 2); +}); + +auto evenInts = std::move(ints) | rpl::filter([](int value) { + return (value % 2 == 0); +}); +``` + +### Combining Producers + +**`rpl::combine`** - combines latest values (lambdas receive unpacked arguments): + +```cpp +auto combined = rpl::combine(countProducer, textProducer); + +std::move(combined) | rpl::on_next([=](int count, const QString &text) { + qDebug() << "Count=" << count << ", Text=" << text; +}, lifetime); +``` + +**`rpl::merge`** - merges producers of same type: + +```cpp +auto merged = rpl::merge(sourceA, sourceB); + +std::move(merged) | rpl::on_next([=](QString &&value) { + qDebug() << "Merged value: " << value; +}, lifetime); +``` + +**Key points:** +- Explicitly `std::move` producers when starting pipelines +- Pass `rpl::lifetime` to `on_...` methods or store returned lifetime +- Use `rpl::duplicate(producer)` to reuse a producer multiple times +- Combined producers automatically unpack tuples in lambdas + diff --git a/CLAUDE.md b/CLAUDE.md index 09ca399310..f8564d18ce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,429 +1,3 @@ -# Claude Code Guide for Telegram Desktop +# Claude Code Pointer -This guide explains how AI assistants should work with the Telegram Desktop codebase. - -## Build System Structure - -The build system expects this directory layout: - -``` -D:\Telegram\ # BuildPath -├── tdesktop/ # Repository (you work here) -├── Libraries/ # 32-bit dependencies (Linux/macOS) -├── win64/ -│ └── Libraries/ # 64-bit dependencies (Windows) -└── ThirdParty/ # Build tools (NuGet, Python, etc.) -``` - -Dependencies are located relative to the repository: `../Libraries`, `../win64/Libraries`, or `../ThirdParty`. - -## Build Configuration - -### Build Commands - -**From repository root, run:** - -```bash -cmake --build out --config Debug --target Telegram -``` - -That's it. The `out/` directory is already configured. The executable will be at `out/Debug/Telegram.exe`. - -**Important:** When running cmake from a shell that doesn't support `cd`, use quoted absolute paths: -```bash -cmake --build "d:\Telegram\tx64\out" --config Debug --target Telegram -``` - -**Never build Release** - it's extremely heavy and not needed for testing changes. - -## Platform-Specific Requirements - -### Windows -- Requires Visual Studio 2022 -- Must run from appropriate Native Tools Command Prompt: - - "x64 Native Tools Command Prompt" for `win64` - - "x86 Native Tools Command Prompt" for `win` - - "ARM64 Native Tools Command Prompt" for `winarm` -- Dependencies: `../win64/Libraries` (64-bit) or `../Libraries` (32-bit) - -### macOS -- Requires Xcode -- Dependencies: `../Libraries/local/Qt-*` -- Set `QT` environment variable: `export QT=6.8` - -### Linux -- Build dependencies in `../Libraries` -- Set `QT` environment variable if needed - -## Key Files - -- **`Telegram/build/version`** - Version information -- **`out/`** - Build output directory - -## Troubleshooting - -### "Libraries not found" -Ensure the repository is in `D:\Telegram\tdesktop`. The build system requires `../win64/Libraries` to exist. - -### Build fails with "wrong command prompt" -On Windows, use the correct Visual Studio Native Tools Command Prompt matching your target (x64/x86/ARM64). - -### Build fails with PDB or EXE access errors - -**⚠️ CRITICAL: DO NOT RETRY THE BUILD. STOP AND WAIT FOR USER.** - -If the build fails with ANY of these errors: -- `fatal error C1041: cannot open program database` -- `cannot open output file 'Telegram.exe'` -- `LNK1104: cannot open file` -- Any "access denied" or "file in use" error - -**STOP IMMEDIATELY.** These errors mean files are locked by a running process (Telegram.exe or debugger). - -**What to do:** -1. Do NOT attempt another build - it will fail the same way -2. Do NOT try to delete files - they are locked -3. Do NOT try any workarounds or fixes -4. IMMEDIATELY inform the user: - -> "Build failed - files are locked. Please close Telegram.exe (and any debugger) so I can rebuild." - -**Then WAIT for user confirmation before attempting any build.** - -Retrying builds wastes time and context. The ONLY fix is for the user to close the running process. - -## Best Practices - -1. **Always use Debug builds** - Release builds are extremely heavy -2. **Don't build Release configuration** - it's too heavy for testing - ---- - -# Development Guidelines - -## Coding Style - -**Do NOT write comments in code:** - -This 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. - -```cpp -// BAD - don't do this: -// Get the user's name -auto name = user->name(); -// Check if premium -if (user->isPremium()) { - -// GOOD - no comments needed, code is self-explanatory: -auto name = user->name(); -if (user->isPremium()) { - -// ACCEPTABLE - complex algorithm explanation (4+ lines): -// The algorithm works by first collecting all visible messages -// in the viewport, then calculating their intersection with -// the clip rectangle. Messages are grouped by date headers, -// and we need to account for sticky headers that may overlap -// with the first message in each group. -``` - -**Empty line before closing brace:** - -Always add an empty line before the closing brace of a class (after all private fields): - -```cpp -// GOOD: -class MyClass { -public: - void foo(); - -private: - int _value = 0; - -}; - -// BAD: -class MyClass { -public: - void foo(); - -private: - int _value = 0; -}; -``` - -**Multi-line expressions — operators at the start of continuation lines:** - -When splitting an expression across multiple lines, place operators (like `&&`, `||`, `;`, `+`, etc.) at the **beginning** of continuation lines, not at the end of the previous line. This makes it immediately obvious from the left edge whether a line is a continuation or new code. - -```cpp -// BAD - continuation looks like scope code: -if (const auto &lottie = animation->lottie; - lottie && lottie->valid() && lottie->framesCount() > 1) { - lottie->animate([=] { - -// GOOD - semicolon at start signals continuation: -if (const auto &lottie = animation->lottie - ; lottie && lottie->valid() && lottie->framesCount() > 1) { - lottie->animate([=] { - -// BAD - trailing && makes next line look like independent code: -if (veryLongExpression() && - anotherLongExpression() && - anotherOne()) { - doSomething(); - -// GOOD - leading && clearly marks continuation: -if (veryLongExpression() - && anotherLongExpression() - && anotherOne()) { - doSomething(); -``` - -**Use `auto` for type deduction:** - -Prefer `auto` (or `const auto`, `const auto &`) instead of explicit types: - -```cpp -// Prefer this: -auto currentTitle = tr::lng_settings_title(tr::now); -auto nameProducer = GetNameProducer(); - -// Instead of this: -QString currentTitle = tr::lng_settings_title(tr::now); -rpl::producer nameProducer = GetNameProducer(); -``` - -## API Usage - -### API Schema Files - -API definitions use [TL Language](https://core.telegram.org/mtproto/TL): - -1. **`Telegram/SourceFiles/mtproto/scheme/mtproto.tl`** - MTProto protocol (encryption, auth, etc.) -2. **`Telegram/SourceFiles/mtproto/scheme/api.tl`** - Telegram API (messages, users, chats, etc.) - -### Making API Requests - -Standard pattern using `api()`, generated `MTP...` types, and callbacks: - -```cpp -api().request(MTPnamespace_MethodName( - MTP_flags(flags_value), - MTP_inputPeer(peer), - MTP_string(messageText), - MTP_long(randomId), - MTP_vector() -)).done([=](const MTPResponseType &result) { - // Handle successful response - - // Multiple constructors - use .match() or check type: - result.match([&](const MTPDuser &data) { - // use data.vfirst_name().v - }, [&](const MTPDuserEmpty &data) { - // handle empty user - }); - - // Single constructor - use .data() shortcut: - const auto &data = result.data(); - // use data.vmessages().v - -}).fail([=](const MTP::Error &error) { - // Handle API error - if (error.type() == u"FLOOD_WAIT_X"_q) { - // Handle flood wait - } -}).handleFloodErrors().send(); -``` - -**Key points:** -- Always refer to `api.tl` for method signatures and return types -- Use generated `MTP...` types for parameters (`MTP_int`, `MTP_string`, etc.) -- For multiple constructors, use `.match()` or check `.type()` then `.c_constructor()` -- For single constructors, use `.data()` shortcut -- Include `.handleFloodErrors()` before `.send()` - -## UI Styling - -### Style Files - -UI styles are defined in `.style` files using custom syntax: - -```style -using "ui/basic.style"; -using "ui/widgets/widgets.style"; - -MyButtonStyle { - textPadding: margins; - icon: icon; - height: pixels; -} - -defaultButton: MyButtonStyle { - textPadding: margins(10px, 15px, 10px, 15px); - icon: icon{{ "gui/icons/search", iconColor }}; - height: 30px; -} - -primaryButton: MyButtonStyle(defaultButton) { - icon: icon{{ "gui/icons/check", iconColor }}; -} -``` - -**Built-in types:** -- `int` - Integer numbers -- `pixels` - Pixel values with `px` suffix (e.g., `10px`) -- `color` - Named colors from `ui/colors.palette` -- `icon` - Inline icon definition: `icon{{ "path/stem", color }}` -- `margins` - Four values: `margins(top, right, bottom, left)` -- `size` - Two values: `size(width, height)` -- `point` - Two values: `point(x, y)` -- `align` - Alignment: `align(center)`, `align(left)` -- `font` - Font: `font(14px semibold)` -- `double` - Floating point - -**Never hardcode sizes in code:** - -The 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::`. - -```cpp -// BAD - breaks at non-100% interface scale: -p.drawText(10, 20, text); -widget->setFixedHeight(48); -auto margin = 8; -auto iconSize = QSize(24, 24); - -// GOOD - define in .style file and reference: -p.drawText(st::myWidgetTextLeft, st::myWidgetTextTop, text); -widget->setFixedHeight(st::myWidgetHeight); -auto margin = st::myWidgetMargin; -auto iconSize = st::myWidgetIconSize; -``` - -### Usage in Code - -```cpp -#include "styles/style_widgets.h" - -// Access style members -int height = st::primaryButton.height; -const style::icon &icon = st::primaryButton.icon; -style::margins padding = st::primaryButton.textPadding; - -// Use in painting -void MyWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - p.fillRect(rect(), st::chatInput.backgroundColor); -} -``` - -## Localization - -### String Definitions - -Strings are defined in `Telegram/Resources/langs/lang.strings`: - -``` -"lng_settings_title" = "Settings"; -"lng_confirm_delete_item" = "Are you sure you want to delete {item_name}?"; -"lng_files_selected#one" = "{count} file selected"; -"lng_files_selected#other" = "{count} files selected"; -``` - -### Usage in Code - -**Immediate (current value):** - -```cpp -auto currentTitle = tr::lng_settings_title(tr::now); - -auto currentConfirmation = tr::lng_confirm_delete_item( - tr::now, - lt_item_name, currentItemName); - -auto filesText = tr::lng_files_selected(tr::now, lt_count, count); -``` - -**Reactive (rpl::producer):** - -```cpp -auto titleProducer = tr::lng_settings_title(); - -auto confirmationProducer = tr::lng_confirm_delete_item( - lt_item_name, - std::move(itemNameProducer)); - -auto filesTextProducer = tr::lng_files_selected( - lt_count, - countProducer | tr::to_count()); -``` - -**Key points:** -- Pass `tr::now` as first argument for immediate `QString` -- Omit `tr::now` for reactive `rpl::producer` -- Placeholders use `lt_tag_name, value` pattern -- For `{count}`: immediate uses `int`, reactive uses `rpl::producer` with `| tr::to_count()` -- Move producers with `std::move` when passing to placeholders - -## RPL (Reactive Programming Library) - -### Core Concepts - -**Producers** represent streams of values over time: - -```cpp -auto intProducer = rpl::single(123); // Emits single value -auto lifetime = rpl::lifetime(); // Manages subscription lifetime -``` - -### Starting Pipelines - -```cpp -std::move(counter) | rpl::on_next([=](int value) { - qDebug() << "Received: " << value; -}, lifetime); - -// Without lifetime parameter - MUST store returned lifetime: -auto subscriptionLifetime = std::move(counter) | rpl::on_next([=](int value) { - // process value -}); -``` - -### Transforming Producers - -```cpp -auto strings = std::move(ints) | rpl::map([](int value) { - return QString::number(value * 2); -}); - -auto evenInts = std::move(ints) | rpl::filter([](int value) { - return (value % 2 == 0); -}); -``` - -### Combining Producers - -**`rpl::combine`** - combines latest values (lambdas receive unpacked arguments): - -```cpp -auto combined = rpl::combine(countProducer, textProducer); - -std::move(combined) | rpl::on_next([=](int count, const QString &text) { - qDebug() << "Count=" << count << ", Text=" << text; -}, lifetime); -``` - -**`rpl::merge`** - merges producers of same type: - -```cpp -auto merged = rpl::merge(sourceA, sourceB); - -std::move(merged) | rpl::on_next([=](QString &&value) { - qDebug() << "Merged value: " << value; -}, lifetime); -``` - -**Key points:** -- Explicitly `std::move` producers when starting pipelines -- Pass `rpl::lifetime` to `on_...` methods or store returned lifetime -- Use `rpl::duplicate(producer)` to reuse a producer multiple times -- Combined producers automatically unpack tuples in lambdas +Read `AGENTS.md` and treat it as the canonical repository-wide instructions.