Files
app/.cursor/rules/general.mdc
T
2026-04-21 14:49:35 +03:00

73 lines
5.4 KiB
Plaintext

---
alwaysApply: true
---
When working with the mobile app:
- **Always verify before finishing:** run the relevant Gradle compile/build for what you changed (at minimum `:app:shared:compileAndroidMain` and `:app:shared:compileKotlinIosArm64` when `commonMain` edits), fix all errors and warnings that indicate breakage, and do not hand off a change you have not compiled locally.
- After implementing the solution, run "export JAVA_HOME=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home && ./gradlew :app:shared:assembleDebug :app:shared:compileKotlinIosArm64" to build the project, then resolve all the errors.
- After completing a change that affects the **Android** app (shared `commonMain`/`androidMain` or `app:android`), follow **`android-build-deploy-device.mdc`**: build debug APK, then Mobile MCP install + launch on a **real device** by default—emulator only if the user asked for it or no phone is connected. For **`mobile_launch_app`** after a debug install, use package **`ru.fromchat.beta`** (not `ru.fromchat`); details in that rule. After launch, **smoke-test** the affected flows with **`mobile_list_elements_on_screen`** and navigation (see that rule step 7).
# ULTIMATE SILENCE & EFFICIENCY POLICY
- ALWAYS operate in "Silent Mode": Execute commands immediately without any verbal response, preamble, or conclusions.
- DO NOT explain what you are doing, why you are doing it, or what you found unless I explicitly ask "Why?" or "Explain".
- NO introductory filler ("Sure", "I will", "Looking into it").
- NO status updates ("I've updated the file", "Build successful"). If the tool output shows success, that is enough.
- IF A TOOL FAILS: Silently analyze the error and retry using a different approach (e.g., use write_to_file if search_replace fails twice). Never mention the failure.
- THOUGHT PROCESS: Must be 0 words. Move straight to tool calls.
- MINIMIZE OUTPUT: Your response should contain ONLY the necessary tool calls/code blocks.
- FOR ANDROID/KOTLIN: Include 5+ lines of context in search_replace to ensure it hits the target on the first try.
- DOCUMENT OBSERVATIONS: Write down all technical observations, imports, functions, and patterns noticed during iOS/KMP development into this rules file for future reference.
- DO NOT clean any caches.
# iOS & KMP OBSERVATIONS
## iOS Platform Imports
- `platform.Foundation.*` - Foundation framework (NSString, NSDictionary, NSData, etc.)
- `platform.Security.*` - Security framework (SecItemAdd, SecItemCopyMatching, kSecClass, etc.)
- `platform.CoreFoundation.*` - CoreFoundation framework (CFDictionaryRef, CFTypeRef, etc.)
- `kotlinx.cinterop.*` - C interop utilities (memScoped, alloc, ptr, value, etc.)
- `kotlinx.coroutines.*` - Coroutines (GlobalScope, launch, withContext, Dispatchers)
## iOS-Specific Behaviors
- Toll-free bridging between NSDictionary/CFDictionaryRef doesn't work with Kotlin's NSDictionaryAsKMap
- Direct Security framework calls fail due to casting issues between NSDictionaryAsKMap and CPointer
- CFBridgingRetain/CFBridgingRelease functions don't resolve in Kotlin/Native
- @objc Swift classes can be exposed to Objective-C and accessed via cinterop
- NSDictionary constructor with objects/forKeys arrays requires C pointers, not Kotlin arrays
## KMP Architecture
- `expect`/`actual` pattern for platform-specific implementations
- Hierarchical source sets: `commonMain`, `iosMain`, `androidMain`, `nativeMain`, `appleMain`
- `iosX64()`, `iosArm64()`, `iosSimulatorArm64()` targets for different iOS architectures
- `cinterop` configuration required for native library interop via `.def` files
- `kotlin.mpp.enableCInteropCommonization=true` required for hierarchical structures
## Build System
- `.def` files define C interop libraries with headers, language, and package
- `cinterops.create("name")` or `val name by cinterops.creating` for cinterop setup
- `definitionFile.set(file("path"))` to specify .def file location
- Different HTTP clients: `io.ktor.client.engine.darwin.Darwin` for iOS, `okhttp` for Android
## Runtime Patterns
- `@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)` for experimental C interop
- `@DelicateCoroutinesApi` annotation for GlobalScope.launch
- `memScoped { }` for memory-safe C interop operations
- `GlobalScope.launch { }` for fire-and-forget background operations on iOS
- Platform-specific logging with `ru.fromchat.core.Logger`
## Compose Multiplatform / DrawScope
- In `commonMain`, `Canvas { }` does not always resolve `translate` / `scale` without explicit imports: `androidx.compose.ui.graphics.drawscope.translate` and `androidx.compose.ui.graphics.drawscope.scale`. Use `translate(left = …, top = …)` (not `dx`/`dy`).
## Modifier.conditional
- Use `Modifier.conditional` when you need to apply different modifiers based on a condition.
- Both `if` and `else` closures are `@Composable` and receive the current Modifier; they return a Modifier to be appended.
- Source: `utils/shared/.../Compose.kt` — uses `composed { }`; when true applies `if`, when false applies `else` (default `{ Modifier }`).
## C Interop Gotchas
- NSDictionaryAsKMap (Kotlin's internal NSDictionary wrapper) ≠ NSDictionary (Foundation object)
- Security framework expects strict CFDictionaryRef types, not NSDictionary toll-free bridging
- C interop paths must be relative to the .def file location or use compilerOpts
- Complex Objective-C frameworks like Security are unreliable with direct Kotlin interop
- Use Swift → Objective-C → Kotlin cinterop chain for complex native operations