[cc] Add CLAUDE.md for the Claude Code agent.

This commit is contained in:
John Preston
2025-12-30 16:15:23 +04:00
parent ab27005dfc
commit d26510a1e9
+373
View File
@@ -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