mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Combine Cursor rules into one, overhaul the server configuration screen
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
---
|
||||
description: After Android/KMP app changes—build debug APK, install and launch on a real device; emulator only if user asked or no phone is connected
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Build, install, and run on device (after Android-related changes)
|
||||
|
||||
Whenever you change anything under **`app:android`**, **`app:shared`** (`commonMain` / `androidMain`), or **`utils:shared`** in ways that affect the Android app:
|
||||
|
||||
1. **Build** the debug APK: `./gradlew :app:android:assembleDebug` from the Android repo root. On macOS, use Android Studio’s bundled JBR (e.g. `JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home"`, or unset `JAVA_HOME` so `gradlew` defaults to it). If that JBR is missing or fails, use another working JDK 21+ (`GRADLE_JAVA_HOME`).
|
||||
2. **Read Mobile MCP tool schemas** under the project’s MCP descriptors for `user-Mobile MCP` before calling tools (required).
|
||||
3. **`mobile_list_available_devices`** — get all `id`s (and `name` / `type` if present).
|
||||
4. **Use a real device by default.** Do **not** install or launch on an emulator **unless** the user **explicitly** asked to use the emulator, **or** no physical device is connected and **only** emulator(s) are available. Classify devices using `type != "emulator"` when reliable; if `type` is wrong or missing, treat as physical when the **name** does not look like an AVD (e.g. not `sdk_gphone`, not a generic emulator name). When one or more physical devices are available, install and launch **only** on those—**never** also push to emulators in the same step. When **no** physical device is connected, use every returned target (emulators).
|
||||
- **All connected phones (e.g. two-device FromChat testing):** When the user asks to **always run on every / both connected devices** (or similar), repeat **`mobile_install_app`** and **`mobile_launch_app`** for **each** connected physical device ID returned by **`mobile_list_available_devices`**, not just one. If only one device is online, use that one; do not fail the step.
|
||||
5. For **each** chosen device `id`: **`mobile_install_app`** with `path` = absolute path to
|
||||
`app/android/build/outputs/apk/debug/android-debug.apk`
|
||||
in this repo.
|
||||
6. For **each** same device: **`mobile_launch_app`** with `packageName` **`ru.fromchat.beta`**. The debug APK from `assembleDebug` uses `applicationIdSuffix = ".beta"` (`app/android/build.gradle.kts`), so the on-device package is **`ru.fromchat.beta`**, not `ru.fromchat`—`ru.fromchat` will fail to launch after a debug install. (Release / no-suffix id is `ru.fromchat`.)
|
||||
7. **Smoke-test after launch** (when a device is online): use **`mobile_list_elements_on_screen`** on the chosen device, then exercise the flows your change touched (e.g. open **Settings**, drill into **Appearance**, **Notifications**, **Devices**, **Security** steps, **Account**, **About**—tap through and use **Back**). Fix any crash or obvious broken UI before finishing.
|
||||
|
||||
Also run **`:app:shared:compileKotlinIosArm64`** (and fix errors) when shared Kotlin changes should stay valid for iOS—either with the same Gradle invocation as in `general.mdc` or right after the Android APK build.
|
||||
|
||||
Skipping install/launch when **no** device is online is acceptable; do not skip the **Gradle build** after code changes.
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
description: Android/KMP workspace rules (single concise source of truth)
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Android / KMP rules
|
||||
|
||||
## Project context (quick)
|
||||
- This is a **messaging app**.
|
||||
- The Kotlin Multiplatform shared code lives under:
|
||||
- `app/shared/src/commonMain/kotlin/` (cross-platform logic + Compose UI)
|
||||
- `app/shared/src/androidMain/kotlin/` (Android-specific implementations)
|
||||
- `app/shared/src/iosMain/kotlin/` (iOS-specific implementations, when present)
|
||||
- Android app module: `app/android/`
|
||||
- Shared UI strings (Compose resources):
|
||||
- `app/shared/src/commonMain/composeResources/values/strings.xml`
|
||||
- `app/shared/src/commonMain/composeResources/values-ru/strings.xml`
|
||||
|
||||
## Build / validation (required before finishing)
|
||||
- After changes, run this command (and fix all errors):
|
||||
- `export JAVA_HOME="/Applications/Android Studio.app/Contents/jbr/Contents/Home" && ./gradlew :app:shared:compileAndroidMain :app:shared:compileKotlinIosArm64`
|
||||
- If it fails, investigate and try to fix the issue yourself (don’t stop at reporting the failure).
|
||||
|
||||
## After Android-affecting changes: build + run on device (Mobile MCP)
|
||||
If you changed anything under `app:android`, `app:shared` (`commonMain` / `androidMain`), or `utils:shared` that affects Android:
|
||||
- **Build** debug APK: `./gradlew :app:android:assembleDebug` (artifact: `app/android/build/outputs/apk/debug/android-debug.apk`).
|
||||
- **Install + launch via Cursor Mobile MCP** (configured name **`Mobile MCP`** in `mcp.json`; in Agent MCP tool calls the server id is often **`user-Mobile MCP`**—use whatever id your session lists for `@mobilenext/mobile-mcp`). **Read each tool’s schema**, then:
|
||||
1. **`mobile_list_available_devices`** — pick the target **Android** `device` id(s) (prefer a **physical** device when validating UI; use an emulator when the task needs it or no phone is listed).
|
||||
2. **`mobile_install_app`** — `device`, `path` = **absolute** path to `android-debug.apk` under the repo (e.g. `<workspace>/app/android/build/outputs/apk/debug/android-debug.apk`).
|
||||
3. **`mobile_launch_app`** — `device`, `packageName` = **`ru.fromchat.beta`** (debug application id; not `ru.fromchat`).
|
||||
- Smoke-test the affected flows after launch.
|
||||
- If **`mobile_install_app`** fails on an **emulator** with insufficient storage, use **“Android emulator storage”** (below), then retry.
|
||||
- Use raw **`adb install` / `am start`** only if Mobile MCP is unavailable after checking MCP status—say so in the reply.
|
||||
|
||||
## Calls (LiveKit / `Call*`, call UI, foreground call service, call audio/video)
|
||||
Whenever the task touches **calls** (e.g. `CallMediaLayer`, `CallForegroundService`, LiveKit wiring, in-call UI, mic/camera/screen-share for calls):
|
||||
- **Build** debug: `./gradlew :app:android:assembleDebug`
|
||||
- **Install + launch with Mobile MCP** on **every Android target** you will use for validation (same three tools as above: **`mobile_list_available_devices`** → **`mobile_install_app`** (`device` + absolute `path` to `android-debug.apk`) → **`mobile_launch_app`** (`device`, `packageName` = **`ru.fromchat.beta`**)). Use the MCP **server id** your environment exposes (e.g. **`user-Mobile MCP`**). Repeat for **each** Android `device` id (emulator **and** phone when both appear in `mobile_list_available_devices`).
|
||||
- Do **not** treat the task as finished until this has been run on **both** an **emulator** and a **physical device** whenever both are available (start/boot the missing one, list devices again, then install + launch on each). If install fails on the emulator, follow **“Android emulator storage”** below, then retry install + launch. If it still fails after a real cleanup attempt, report what you tried.
|
||||
- If only one class of device is listed, deploy to every listed Android device and state clearly what was missing.
|
||||
|
||||
## Android emulator storage (no approval needed)
|
||||
When an **Android emulator** hits **`INSTALL_FAILED_INSUFFICIENT_STORAGE`** or otherwise has too little free space for a debug install, you may **do whatever is needed to free space on that emulator only** without asking the user first: uninstall third-party apps (`adb -s <emu> uninstall …`), clear caches (`adb shell pm trim-caches` / `rm` under emulator-owned paths the shell can reach), delete arbitrary files **inside that AVD**, or **wipe the AVD / `emulator -wipe-data`** if that is the fastest fix. Treat the emulator as disposable dev state.
|
||||
- **Never** use this “anything goes” approach on a **physical device** (only uninstall/clear what the user explicitly asked for, or use normal Mobile MCP flows).
|
||||
|
||||
## Never touch Gradle caches
|
||||
- Do not read, list, search, copy, or modify anything under Gradle cache paths (e.g. `~/.gradle/caches`, `**/.gradle/caches`).
|
||||
|
||||
## No magic-string “sanitization” of real data
|
||||
- Never strip/null/rewrite stored or displayed values by comparing to hard-coded UI/placeholder strings.
|
||||
- Fix at the source (don’t persist placeholders) or use explicit migrations/sentinels—never natural-language matching.
|
||||
|
||||
## UI strings (shared module)
|
||||
- No hardcoded user-visible copy in shared UI. Use Compose Multiplatform resources:
|
||||
- `app/shared/src/commonMain/composeResources/values/strings.xml` (default)
|
||||
- `app/shared/src/commonMain/composeResources/values-ru/strings.xml` (Russian)
|
||||
- Keep keys in sync across both files.
|
||||
- Exception: Debug API screen (`ru.fromchat.ui.debug`) may hardcode strings.
|
||||
|
||||
## Prefer utils package APIs
|
||||
Prefer `com.pr0gramm3r101.utils` when available (before custom solutions), especially:
|
||||
- Clipboard: `supportClipboardManagerImpl` / `SupportClipboardManager.setText`
|
||||
- Strings: `String.toAnnotatedString()`
|
||||
- Compose: `Modifier.conditional`, `CompositionLocal.invoke()`
|
||||
|
||||
## Debug HTTP logging
|
||||
- Prefer Ktor `HttpClient` for debug/instrumentation HTTP logging.
|
||||
- Do not use `HttpURLConnection` or raw `OkHttpClient` for debug-only logging.
|
||||
- Centralize behind `ru.fromchat.debug.DebugLogger`, best-effort only (never crash the app).
|
||||
|
||||
## Codebase hygiene
|
||||
- Prefer **official docs** first for typical Jetpack Compose / Compose Multiplatform patterns; if docs cover it, follow them.
|
||||
- Avoid breaking iOS via Android-only APIs in `commonMain` (keep platform code in `androidMain` / `iosMain` as needed).
|
||||
- Do not introduce hardcoded user-visible strings outside the Debug API exception (see “UI strings” above).
|
||||
|
||||
## Skills (when to use)
|
||||
- Use the **Material 3 skill** when implementing or changing Compose UI using Material3 components, theming, tokens, or accessibility.
|
||||
- Use the **using-computer skill** only when the user asks me to control the computer / click through UI / take screenshots.
|
||||
- Use **Cursor “create-rule/create-skill/create-hook” skills** only when you explicitly ask to create or modify Cursor rules/skills/hooks.
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
description: Use Ktor HttpClient for debug-mode logging instead of platform APIs
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Debug-mode logging with Ktor
|
||||
|
||||
- Prefer **Ktor `HttpClient`** for all debug/instrumentation HTTP logging in this project.
|
||||
- **Do NOT** use `HttpURLConnection`, raw `OkHttpClient`, or other low-level networking APIs for debug-only logging.
|
||||
- Centralize debug logging behind `ru.fromchat.debug.DebugLogger` and keep it **best-effort only** (never throw, never crash the app).
|
||||
- When adding new debug logging:
|
||||
- Use a shared `HttpClient` instance (per platform) configured with the appropriate engine (e.g. OkHttp on Android).
|
||||
- Send JSON NDJSON-style payloads to the configured debug endpoint.
|
||||
- Avoid blocking the main thread; run network I/O in coroutines on a background dispatcher.
|
||||
- Keep debug logging code small and self-contained so it is easy to remove or disable when no longer needed.
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
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
|
||||
@@ -1,22 +0,0 @@
|
||||
---
|
||||
description: Never touch Gradle caches — no reads, writes, or searches there
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Gradle caches — never touch
|
||||
|
||||
The agent must **never interact with Gradle cache directories** in any way.
|
||||
|
||||
## Forbidden (including “read-only”)
|
||||
|
||||
- **Do not** read, open, list, traverse, copy, move, delete, or modify anything under Gradle cache paths (e.g. `~/.gradle/caches`, `**/.gradle/caches`, Gradle user home caches, transform outputs, jars that exist only under caches).
|
||||
- **Do not** run `grep`, `ripgrep` (`rg`), `find`, `ls`, glob tools, or **any** search or directory listing **inside** those paths — even for “read-only” research.
|
||||
- **Do not** cite cache paths as the source of truth for framework or dependency source code.
|
||||
|
||||
## Use instead
|
||||
|
||||
- Project source under the **workspace**
|
||||
- Official documentation and dependency coordinates in **`gradle/libs.versions.toml`** / **`*.gradle.kts`**
|
||||
- IDE navigation in the repo
|
||||
|
||||
Normal Gradle builds on the developer machine may still use the daemon and local caches; this rule governs **agent** behavior only.
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
description: Do not filter cache, API, or DB fields by matching fixed UI/placeholder English strings
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# No magic-string “sanitization” of user or message data
|
||||
|
||||
- **Never** strip, null out, or rewrite stored or displayed values by comparing them to hard-coded UI strings (e.g. `"Direct messages"`, `"Direct message"`, `"User 123"`, etc.). Those strings can be legitimate **usernames, display names, or message text**.
|
||||
- **Prefer**: fix the source (don’t persist placeholders; use `null`/absent fields; fix the writer). If legacy bad rows exist, use an explicit **schema/version/migration** or a **documented sentinel** agreed with the backend—not substring or equality checks on natural language.
|
||||
- Applies especially to: SQLDelight/cache layers, list previews, and any code that “cleans” strings before show or read.
|
||||
@@ -1,17 +0,0 @@
|
||||
---
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# UI strings (shared module)
|
||||
|
||||
## No hardcoded user-visible text
|
||||
- Do not put English or Russian (or other) UI copy directly in Kotlin/Compose for screens users see. Use Compose Multiplatform resources: `app/shared/src/commonMain/composeResources/values/strings.xml` (default English) and `values-ru/strings.xml` (Russian), **same `name` keys** in both files.
|
||||
- Use `stringResource(Res.string.*)` in `@Composable` (and `import ru.fromchat.*` when needed so `Res.string` extensions resolve). Internal-only tokens (cache keys, shared-element keys, protocol constants) may stay in code.
|
||||
|
||||
## When you add or change copy
|
||||
- Add or update the string in **both** `values/strings.xml` and `values-ru/strings.xml` in the **same** change.
|
||||
- Prefer **plain language** short words, short sentences; avoid jargon unless necessary (e.g. say “server address” rather than “URL” in labels when it fits).
|
||||
|
||||
## Debug API screen — excluded
|
||||
- The **Debug API** screen (`ru.fromchat.ui.debug`) is not for standard users. You may **hardcode English (or any) strings directly in Kotlin** for that screen and **do not** need to add matching keys in `values/strings.xml` / `values-ru/strings.xml` or keep translations in sync.
|
||||
- The **entry row** that opens Debug API (e.g. in Settings) stays in compose resources like other settings copy.
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
description: Prefer utils package APIs when available
|
||||
globs: "**/*.kt"
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Utils Package Preference
|
||||
|
||||
When working with the Android/KMP codebase, prefer APIs from `com.pr0gramm3r101.utils` whenever possible.
|
||||
|
||||
## Clipboard
|
||||
- Use `supportClipboardManagerImpl` (Composable) instead of `LocalClipboardManager` / `LocalClipboard`
|
||||
- `SupportClipboardManager.setText(string: String)` is suspend; call from `scope.launch { }`
|
||||
|
||||
## String
|
||||
- Use `String.toAnnotatedString()` for `AnnotatedString` conversion
|
||||
|
||||
## Modifier
|
||||
- Use `Modifier.conditional` from `com.pr0gramm3r101.utils.Compose` for conditional modifiers
|
||||
|
||||
## CompositionLocal
|
||||
- Use `CompositionLocal.invoke()` for `current` when available
|
||||
|
||||
## Other
|
||||
- Check `Utils.kt`, `Compose.kt`, `Components.kt`, `Adaptive.kt` before implementing custom solutions
|
||||
Reference in New Issue
Block a user