mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
[cc] Add CLAUDE.md for the Claude Code agent.
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
# Claude Code Guide for Telegram Desktop
|
||||
|
||||
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
|
||||
If the build fails with errors like:
|
||||
- `cannot open program database '...vc143.pdb'`
|
||||
- `cannot open output file 'Telegram.exe'`
|
||||
|
||||
**Do NOT retry the build repeatedly.** First check if `out/Debug/Telegram.exe` can be deleted:
|
||||
```bash
|
||||
rm out/Debug/Telegram.exe
|
||||
```
|
||||
If deletion fails, the user is likely running Telegram.exe for testing. Inform the user:
|
||||
> "Build failed due to file lock. Please close Telegram.exe so I can complete the build."
|
||||
|
||||
Do not attempt to fix this automatically - wait for user confirmation.
|
||||
|
||||
## 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;
|
||||
};
|
||||
```
|
||||
|
||||
**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<QString> 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<MTPMessageEntity>()
|
||||
)).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
|
||||
|
||||
### 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<QString>`
|
||||
- Placeholders use `lt_tag_name, value` pattern
|
||||
- For `{count}`: immediate uses `int`, reactive uses `rpl::producer<float64>` 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
|
||||
Reference in New Issue
Block a user