# Fluffytrix Agent Guidelines ## Project Overview Fluffytrix is an Android Matrix chat client with a Discord-like UI. Built with Kotlin, targeting Android 14+ (minSdk 34, targetSdk 36, compileSdk 36). **Package**: `com.example.fluffytrix` **Build system**: Gradle with Kotlin DSL, version catalog at `gradle/libs.versions.toml` **Container/DI**: Koin **State management**: Jetpack Compose StateFlow, ViewModel **UI framework**: Jetpack Compose with Material 3 (Dynamic Colors) **Protocol**: Trixnity SDK for Matrix **Storage**: Room Database, DataStore Preferences **Async**: Kotlin Coroutines --- ## Build Commands ```bash ./gradlew assembleDebug # Build debug APK (minified for performance) ./gradlew assembleRelease # Build release APK ./gradlew test # Run unit tests ./gradlew connectedAndroidTest # Run instrumented tests on device/emulator ./gradlew testDebugUnitTest --tests "com.example.fluffytrix.ExampleUnitTest" # Run single test ``` **Notes**: - Debug builds use R8 with `isDebuggable = true` but strip material-icons-extended and optimize Compose for performance - Use `--tests` with Gradle test tasks to run a single test class - Instrumented tests require a connected device or emulator --- ## Testing **Unit tests**: Located in `app/src/test/java/`, run with `./gradlew test` **Instrumented tests**: Located in `app/src/androidTest/java/`, run with `./gradlew connectedAndroidTest` **Single test execution**: ```bash ./gradlew testDebugUnitTest --tests "com.example.fluffytrix.*" ./gradlew app:testDebugUnitTest --tests "ExampleUnitTest" ``` --- ## Code Style Guidelines ### Kotlin Conventions - **Android Official Kotlin style** (`kotlin.code.style=official` in gradle.properties) - File naming: `PascalCase.kt` (e.g., `MainViewModel.kt`) - Class naming: `PascalCase` (e.g., `MainViewModel`, `AuthRepository`) - Function/property naming: `camelCase` (e.g., `sendMessage`, `selectedChannel`) - Constant naming: `PascalCase` for top-level constants - **Never use underscores in variable names** ### Imports - Explicit imports only (no wildcard imports) - Group imports: Android/X → Kotlin → Javax/Java → Third-party → Same package - Example: ```kotlin import android.os.Bundle import androidx.compose.material3.Text import kotlinx.coroutines.flow.StateFlow import net.folivo.trixnity.client.MatrixClient import com.example.fluffytrix.ui.theme.FluffytrixTheme ``` ### Types - Prefer `val` over `var` (immutable data) - Use `StateFlow` for observable state in ViewModels - Use `Flow` for read-only data streams - Use `suspend` functions for async operations - Use `Result` for operations that can fail ### Error Handling - Prefer `try-catch` with silent failure or `?` operators where appropriate - In ViewModels, use `catch (_: Exception) { }` or `?:` for graceful degradation - Expose error state via `StateFlow` where users need feedback - **Never crash the app on recoverable errors** ### Compose UI - Use `@Composable` for all UI functions - Follow Discord-like layout: space sidebar → channel list → message area → member list - Use `MaterialTheme` for consistent theming - Prefer `Modifier.padding()` over nested `Box` with margins - Use `wrapContentWidth()` and `fillMaxWidth()` appropriately - For columnar layouts: `Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp))` ### ViewModels - Inject dependencies via constructor (Koin) - Use `viewModelScope` for coroutines - Expose state via `StateFlow` (e.g., `val messages: StateFlow>`) - Use `MutableStateFlow` for internal state, expose as read-only `StateFlow` ### Coroutines - Use `Dispatchers.Default` for CPU-intensive work (parsing, filtering) - Use `Dispatchers.IO` for file/network operations - Always cancel jobs on ViewModel cleanup: `job?.cancel()` - Use `withContext(Dispatchers.Default)` to switch threads explicitly ### Data Layer - Use Room for persistent storage (Trixnity uses Room internally) - Use DataStore Preferences for small key-value data - Prefer Flow-based APIs for reactive data streams - Cache expensive operations in ViewModel (e.g., `messageCache`, `memberCache`) ### Naming Conventions - State Flow properties: `_name` (private) / `name` (public) - Repository class: `AuthService`, `AuthRepository` - ViewModel class: `MainViewModel`, `LoginViewModel` - UI composable: `MainScreen`, `ChannelList`, `MessageItem` - Model data class: `MessageItem`, `ChannelItem`, `SpaceItem` - Use `full` property for Matrix IDs (e.g., `userId.full`, `roomId.full`) --- ## Architecture Patterns **Layered Architecture**: ``` ui/ — ViewModels, Screens, Navigation data/ — Repositories, local storage, models di/ — Koin modules ui/theme/ — Material 3 Theme (colors, typography) ``` **Dependency Injection**: Koin with two modules: - `appModule`: ViewModels (`viewModel { MainViewModel(...) }`) - `dataModule`: singleton services (`single { AuthRepository(...) }`) **UI Flow**: 1. `FluffytrixNavigation` handles nav graph and session restoration 2. `MainActivity` hosts the NavHost with `FluffytrixTheme` 3. ViewModels expose state via StateFlow 4. Screens observe state with `collectAsState()` --- ## Key Dependencies (from libs.versions.toml) - **Compose BOM**: `2025.06.00` - **Kotlin**: `2.2.10` - **AGP**: `9.0.1` - **Koin**: `4.1.1` - **Trixnity**: `4.22.7` - **Ktor**: `3.3.0` - **Coroutines**: `1.10.2` - **DataStore**: `1.1.7` - **Coil**: `3.2.0` - **Media3**: `1.6.0` --- ## Special Notes 1. **Matrix IDs**: Use `RoomId`, `UserId` types from Trixnity; access `.full` for string representation 2. **MXC URLs**: Convert with `MxcUrlHelper.mxcToDownloadUrl()` and `mxcToThumbnailUrl()` 3. **Build Performance**: Debug builds use R8 minification with `isDebuggable = true` to balance speed and debuggability 4. **Channel Reordering**: Channel order is saved per-space in DataStore and restored on navigation 5. **Encrypted Rooms**: Handle decryption state—messages may appear as `Unable to decrypt` until keys arrive 6. **Theme**: Material 3 with Discord-inspired color scheme (primary: `#5865F2`) --- ## Running Lint/Checks ```bash ./gradlew lintDebug ./gradlew spotlessCheck ``` ## CI/CD - All builds use Gradle wrapper (`./gradlew`) - No manual Gradle installation required (project uses Gradle 9.1.0)