Merge pull request #8 from garfiec/chore/rename-to-mobile

Rename project from LibreChat Android to LibreChat Mobile
This commit is contained in:
Garfie Chiu 2026-04-03 16:57:52 -05:00 committed by GitHub
commit 9be9a57b9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
161 changed files with 406 additions and 403 deletions

View file

@ -1,8 +1,8 @@
---
name: sync-upstream
description: >
Sync the LibreChat Android client with a newer official LibreChat server release.
Diffs upstream tags, identifies gaps in the Android app, proposes changes with
Sync the LibreChat Mobile client with a newer official LibreChat server release.
Diffs upstream tags, identifies gaps in the mobile app, proposes changes with
user approval, then implements them. Use when a new LibreChat version is released.
allowed-tools: Bash, Read, Glob, Grep, Write, Edit, Agent, TeamCreate, SendMessage, TaskCreate, TaskUpdate, TaskGet, TaskList
argument-hint: "[target-tag] (optional, defaults to latest stable)"
@ -10,7 +10,7 @@ argument-hint: "[target-tag] (optional, defaults to latest stable)"
# Sync Upstream
Synchronize the LibreChat Android app with a newer version of the official LibreChat server.
Synchronize the LibreChat Mobile app with a newer version of the official LibreChat server.
This is a multi-phase, team-based workflow.
You are the **team lead**. You orchestrate at a high level. You NEVER read code, write code,
@ -22,18 +22,20 @@ Create an Agent Team with 4 teammates at the start:
| Teammate | Role | Prompt Summary |
|----------|------|----------------|
| **investigator** | Reads upstream diffs, analyzes API/type/UI changes, cross-references with Android codebase | "You are the investigator for a LibreChat Android upstream sync. Your job is to analyze diffs between upstream tags and identify gaps in the Android client. You have read-only responsibilities — never write code." |
| **android-expert** | Technical advisor on Android architecture and best practices. Cross-checks all guidance with online research before advising. | "You are the Android tech lead for a LibreChat Android upstream sync. You provide guidance on Android best practices, Jetpack Compose patterns, Kotlin idioms, and architectural decisions. CRITICAL: You MUST cross-check your knowledge by researching online (WebSearch/WebFetch) before giving advice. Never rely solely on training data — always verify current best practices, especially for Compose, Hilt, Ktor, and Material 3. Return researched guidance to teammates who ask." |
| **implementer** | Writes code changes to Android app following existing patterns and conventions | "You are the implementer for a LibreChat Android upstream sync. Your job is to write code changes following existing architecture patterns. Always read the module's CLAUDE.md before touching it. Follow safeApiCall, Hilt DI, UDF, Ktor, @Serializable patterns. Before making significant architectural decisions, consult the android-expert teammate." |
| **verifier** | Checks implementation correctness, runs builds, validates changes match proposal | "You are the verifier for a LibreChat Android upstream sync. Your job is to independently validate that implementation matches the approved proposal. Run builds, review changes, and flag issues. Message the implementer directly if fixes are needed." |
| **investigator** | Reads upstream diffs, analyzes API/type/UI changes, cross-references with mobile codebase | "You are the investigator for a LibreChat Mobile upstream sync. Your job is to analyze diffs between upstream tags and identify gaps in the mobile client. You have read-only responsibilities — never write code." |
| **android-expert** | Technical advisor on Android architecture and best practices. Cross-checks all guidance with online research before advising. | "You are the Android tech lead for a LibreChat Mobile upstream sync. You provide guidance on Android best practices, Jetpack Compose patterns, Kotlin idioms, and architectural decisions. CRITICAL: You MUST cross-check your knowledge by researching online (WebSearch/WebFetch) before giving advice. Never rely solely on training data — always verify current best practices, especially for Compose, Ktor, and Material 3. Return researched guidance to teammates who ask." |
| **ios-kmp-expert** | Technical advisor on iOS and Kotlin Multiplatform architecture. Ensures KMP shared code and iOS-specific implementations follow platform conventions. | "You are the iOS/KMP tech lead for a LibreChat Mobile upstream sync. You provide guidance on Compose Multiplatform patterns, KMP expect/actual declarations, iOS platform APIs (UIKit, Foundation, Keychain), SKIE interop, and ensuring shared code compiles correctly for both targets. CRITICAL: You MUST cross-check your knowledge by researching online (WebSearch/WebFetch) before giving advice. Never rely solely on training data — always verify current best practices for CMP, KMP, and iOS platform conventions. Return researched guidance to teammates who ask." |
| **implementer** | Writes code changes to mobile app following existing patterns and conventions | "You are the implementer for a LibreChat Mobile upstream sync. Your job is to write code changes following existing architecture patterns. Always read the module's CLAUDE.md before touching it. Follow safeApiCall, Koin DI, UDF, Ktor, @Serializable patterns. Before making significant architectural decisions, consult the android-expert or ios-kmp-expert teammate." |
| **verifier** | Checks implementation correctness, runs builds, validates changes match proposal | "You are the verifier for a LibreChat Mobile upstream sync. Your job is to independently validate that implementation matches the approved proposal. Run builds, review changes, and flag issues. Message the implementer directly if fixes are needed." |
**Teammate interaction pattern:**
- **investigator** works independently on Phase A and B (diff + gap analysis)
- **android-expert** is consulted by the implementer before architectural decisions and by the
lead when the proposal involves patterns that may have better modern alternatives. The
android-expert MUST search online to verify recommendations — do not accept advice that
- **android-expert** is consulted for Android-specific architecture, Compose, and Material 3 decisions.
The android-expert MUST search online to verify recommendations — do not accept advice that
hasn't been cross-checked against current documentation.
- **implementer** does all code changes, consulting android-expert for non-trivial patterns
- **ios-kmp-expert** is consulted for KMP shared code, expect/actual patterns, iOS platform APIs,
and Compose Multiplatform concerns. Same online-verification requirement as android-expert.
- **implementer** does all code changes, consulting android-expert or ios-kmp-expert for non-trivial patterns
- **verifier** validates after implementer finishes, messages implementer for fixes
**Important:** Teammates retain context across messages. Reuse them for follow-ups — do NOT
@ -45,7 +47,7 @@ Run these checks yourself (lightweight, no codebase reading):
### 1. Submodule check
```bash
cd LibreChat-Android && git submodule status
cd Librechat-Mobile && git submodule status
```
If `upstream/` is missing:
```bash
@ -61,7 +63,7 @@ If missing, create from current state:
```bash
VERSION=$(grep 'SUPPORTED_BACKEND_VERSION' core/common/src/main/kotlin/com/garfiec/librechat/core/common/BackendVersion.kt | grep -o '"[^"]*"' | tr -d '"')
COMMIT=$(cd upstream && git rev-parse HEAD)
echo "# Upstream LibreChat version this Android build tracks." > UPSTREAM_VERSION
echo "# Upstream LibreChat version this mobile build tracks." > UPSTREAM_VERSION
echo "# Updated by the sync-upstream skill. Do not edit manually." >> UPSTREAM_VERSION
echo "tag=v${VERSION}" >> UPSTREAM_VERSION
echo "commit=${COMMIT}" >> UPSTREAM_VERSION
@ -126,7 +128,7 @@ Create a task for the investigator with this prompt:
> - **API changes**: New/modified/removed routes, controller logic
> - **Type/schema changes**: New/modified data types, request/response shapes
> - **UI changes**: New components, modified user flows
> - **Bug fixes**: Fixes that may need Android equivalents
> - **Bug fixes**: Fixes that may need mobile equivalents
> - **Infrastructure**: Build, config, deps (usually not relevant)
> 6. Report back a structured summary table.
@ -149,14 +151,14 @@ Once investigator reports back:
### B1. Send follow-up to investigator
> Now cross-reference the upstream changes you found with the Android codebase.
> Now cross-reference the upstream changes you found with the mobile codebase.
>
> 1. Read these mapping files from `${CLAUDE_SKILL_DIR}/reference/`:
> - `api-mapping.md` — official routes to Android *Api.kt files
> - `api-mapping.md` — official routes to mobile *Api.kt files
> - `model-mapping.md` — official TS types to Kotlin data classes
> - `ui-mapping.md` — web components to Android feature modules
> - `ui-mapping.md` — web components to mobile feature modules
> 2. For each API change from Phase A:
> - Grep the Android codebase (`core/network/src/`) for the corresponding endpoint
> - Grep the mobile codebase (`core/network/src/`) for the corresponding endpoint
> - Note if it exists, needs updating, or is missing
> 3. For each type/schema change:
> - Check `core/model/src/` for matching Kotlin data class
@ -168,11 +170,11 @@ Once investigator reports back:
> - **Breaking** (must fix): Changed request/response shapes, removed/renamed endpoints, auth changes
> - **Additive** (new feature): New endpoints, new UI features, new optional fields
> - **Cosmetic** (polish): UI improvements, a11y, i18n
> 6. Report the categorized gap list with specific file paths for both upstream and Android.
> 6. Report the categorized gap list with specific file paths for both upstream and mobile.
### B2. Receive gap report
The investigator now has full context of both the upstream diff AND the Android gaps.
The investigator now has full context of both the upstream diff AND the mobile gaps.
Keep the investigator alive for follow-up questions during Phase C.
**Done when:** You have the categorized gap list from the investigator.
@ -191,42 +193,43 @@ Take the investigator's gap report and present it as a structured proposal:
### Breaking Changes ({count}) — must fix before updating version
For each:
- What changed upstream (with file reference)
- What Android file(s) need updating
- What Mobile file(s) need updating
- Proposed change
### New Features ({count}) — recommended
For each:
- What was added upstream
- Proposed Android implementation approach
- Proposed mobile implementation approach
- Which module(s) would be affected
### UI Changes Needing User Input ({count})
For each:
- What the web app does (with file reference)
- Why it doesn't translate directly to Android/Compose
- Why it doesn't translate directly to mobile/Compose
- 2-3 concrete options for the user to choose from
### Deferred Items ({count}) — can wait for a future sync
Items that are low priority or require significant new infrastructure.
```
### Consult android-expert on non-trivial items
### Consult experts on non-trivial items
Before presenting to the user, for any proposed change that involves:
- New architectural patterns (e.g., adding a new module, new DI scope)
- UI patterns that don't have an existing equivalent in the codebase
- Performance-sensitive changes (e.g., new list rendering, image loading)
- Navigation changes
- KMP shared code or expect/actual patterns
Send these items to the **android-expert** teammate:
Send Android-specific items to **android-expert** and KMP/iOS items to **ios-kmp-expert**:
> Review these proposed Android changes for best practices. For each item,
> Review these proposed mobile changes for best practices. For each item,
> research online to verify the recommended approach is current (especially
> for Compose, Material 3, Hilt, and Ktor). Flag any items where modern
> for Compose, Material 3, Koin, and Ktor). Flag any items where modern
> best practices differ from what we're proposing.
> {list of non-trivial items}
Incorporate the android-expert's feedback into the proposal before showing the user.
Incorporate the experts' feedback into the proposal before showing the user.
### Approval gate
@ -268,8 +271,8 @@ Create a task for the implementer with the approved change list:
> - Ktor client patterns in `core/network/`
>
> **Before making non-trivial architectural decisions** (new patterns, new modules,
> complex UI components), message the **android-expert** teammate for guidance.
> The android-expert will research current best practices online and advise.
> complex UI components), message the **android-expert** or **ios-kmp-expert** teammate for guidance.
> They will research current best practices online and advise.
>
> **Approved changes:**
> {paste the approved change list here}

View file

@ -1,6 +1,6 @@
# Android Architecture Reference
# LibreChat Mobile Architecture Reference
Condensed architecture guide for the LibreChat Android app. Read module-specific `CLAUDE.md` files for full details.
Condensed architecture guide for the LibreChat Mobile app. Read module-specific `CLAUDE.md` files for full details.
## Module Dependency Graph
@ -143,7 +143,7 @@ Two-phase protocol:
- Room entity: `{Domain}Entity.kt`
- Room DAO: `{Domain}Dao.kt`
- DI module: `{Domain}Module.kt`
- Convention plugins: `librechat.android.{type}` (e.g., `librechat.android.feature`)
- Convention plugins: `librechat.mobile.{type}` (e.g., `librechat.mobile.feature`)
## Build System

View file

@ -1,10 +1,10 @@
# API Route Mapping: Official → Android
# API Route Mapping: Official → Mobile
Maps official LibreChat API route files to their Android `*Api.kt` counterparts.
Maps official LibreChat API route files to their mobile `*Api.kt` counterparts.
## Implemented
| Official Route | Android API File | Notes |
| Official Route | Mobile API File | Notes |
|---------------|-----------------|-------|
| `routes/auth.js` | `AuthApi.kt` | Login, register, refresh, logout, 2FA |
| `routes/oauth.js` | `AuthApi.kt` | OAuth flows (shared file) |
@ -28,15 +28,15 @@ Maps official LibreChat API route files to their Android `*Api.kt` counterparts.
| N/A | `SpeechApi.kt` | Text-to-speech |
| N/A | `ApiKeysApi.kt` | API key management |
## Gaps (no Android counterpart yet)
## Gaps (no mobile counterpart yet)
| Official Route | What It Does | Priority |
|---------------|-------------|----------|
| `routes/categories.js` | Agent categories | Low — categories embedded in agent responses |
| `routes/endpoints.js` | List available endpoints | Medium — Android uses config response instead |
| `routes/models.js` | List available models per endpoint | Medium — Android uses config response instead |
| `routes/endpoints.js` | List available endpoints | Medium — Mobile uses config response instead |
| `routes/models.js` | List available models per endpoint | Medium — Mobile uses config response instead |
| `routes/roles.js` | Role-based access control (admin) | Low — admin feature |
| `routes/settings.js` | User settings CRUD | Medium — Android uses local DataStore |
| `routes/settings.js` | User settings CRUD | Medium — Mobile uses local DataStore |
| `routes/actions.js` | Custom actions/plugins | Low — plugin system |
| `routes/assistants/` (dir) | OpenAI Assistants API | Low — agents supersede assistants |
| `routes/accessPermissions.js` | File access permissions | Low — admin feature |

View file

@ -1,10 +1,10 @@
# UI Mapping: Official React Components → Android Feature Modules
# UI Mapping: Official React Components → Mobile Feature Modules
Maps web client component directories to Android Compose feature modules.
Maps web client component directories to mobile Compose feature modules.
## Implemented
| Web Component Dir | Android Module | Coverage |
| Web Component Dir | Mobile Module | Coverage |
|-------------------|---------------|----------|
| `components/Chat/` | `feature/chat/` | Core chat UI, message list, input |
| `components/Messages/` | `feature/chat/` | Message rendering, branching, siblings |
@ -17,11 +17,11 @@ Maps web client component directories to Android Compose feature modules.
| `components/Banners/` | `app/` | System banner display |
| `components/Endpoints/` | `feature/chat/` | Model/endpoint selector bottom sheet |
## Gaps (no Android counterpart)
## Gaps (no mobile counterpart)
| Web Component Dir | What It Does | Priority | Notes |
|-------------------|-------------|----------|-------|
| `components/Prompts/` | Prompts library with CRUD, categories, sharing | Medium | Android has PromptsApi but no dedicated UI |
| `components/Prompts/` | Prompts library with CRUD, categories, sharing | Medium | Mobile has PromptsApi but no dedicated UI |
| `components/Artifacts/` | Code/document artifact viewer and editor | Medium | New feature in recent versions |
| `components/MCP/` | MCP server configuration UI | Low | Admin-oriented feature |
| `components/MCPUIResource/` | MCP UI resource rendering | Low | Depends on MCP support |
@ -33,13 +33,13 @@ Maps web client component directories to Android Compose feature modules.
| `components/Audio/` | Voice input/output, TTS | Medium | SpeechApi exists, no UI |
| `components/Plugins/` | Legacy plugin system | N/A | Superseded by agents |
| `components/Tools/` | Tool configuration and management | Low | Part of agent setup |
| `components/OAuth/` | OAuth provider buttons | Low | Android uses Chrome Custom Tabs |
| `components/OAuth/` | OAuth provider buttons | Low | Mobile uses Chrome Custom Tabs |
| `components/Web/` | Web search integration | Low | Agent tool feature |
| `components/System/` | System-level UI (error boundaries, etc.) | Low | Android has own error handling |
| `components/System/` | System-level UI (error boundaries, etc.) | Low | Mobile has own error handling |
## Key Web Patterns → Android Equivalents
## Key Web Patterns → Mobile Equivalents
| Web Pattern | Android Equivalent |
| Web Pattern | Mobile Equivalent |
|------------|-------------------|
| Recoil atoms (`store/`) | ViewModel state (StateFlow) |
| React Query hooks (`hooks/`) | Repository + ViewModel |

View file

@ -1,13 +1,13 @@
# Upstream Paths to Watch
These are the directories in the `upstream/` submodule that matter for Android parity.
These are the directories in the `upstream/` submodule that matter for mobile parity.
Use these paths when generating focused diffs between tags.
| Path | What It Contains | Why It Matters |
|------|-----------------|----------------|
| `api/server/routes/` | Express route definitions (REST endpoints) | Defines the API contract the Android app calls |
| `api/server/routes/` | Express route definitions (REST endpoints) | Defines the API contract the mobile app calls |
| `api/server/controllers/` | Request handlers and business logic | Shows request/response shapes and validation |
| `packages/data-provider/src/` | Shared types, schemas, API client, config | Canonical type definitions; Android models must match |
| `packages/data-provider/src/` | Shared types, schemas, API client, config | Canonical type definitions; Mobile models must match |
| `packages/data-provider/src/types/` | TypeScript type subdirectory (queries, mutations, agents, etc.) | React Query hook types that define API shapes |
| `packages/data-schemas/src/` | Mongoose schemas and DB models | Database schema changes that affect API responses |
| `client/src/components/` | React UI components by feature area | Reference for Android UI feature parity |

View file

@ -1,6 +1,6 @@
# LibreChat Android
# LibreChat Mobile
Native Android client for LibreChat. Connects to existing LibreChat backend servers (no backend changes). Users specify server URL during onboarding.
Native mobile client for LibreChat (Android & iOS). Connects to existing LibreChat backend servers (no backend changes). Users specify server URL during onboarding.
## Tech Stack
@ -42,7 +42,7 @@ Each module has its own `CLAUDE.md` with specific guidance.
## Adding a New Feature Module
1. Create the module directory under `feature/`
2. Apply the `librechat.android.feature` convention plugin in `build.gradle.kts` — this auto-applies Koin + Compose deps
2. Apply the `librechat.mobile.feature` convention plugin in `build.gradle.kts` — this auto-applies Koin + Compose deps
3. Create `di/<Feature>Module.kt` with a Koin `module { }` containing `viewModelOf(::YourViewModel)` definitions
4. Register the module in `LibreChatApplication.kt`'s `startKoin { modules(...) }` list
5. Use `koinViewModel()` in screen composables to inject ViewModels
@ -58,6 +58,6 @@ Each module has its own `CLAUDE.md` with specific guidance.
## Upstream Sync
- **`upstream/`** — Git submodule of the [official LibreChat repo](https://github.com/danny-avila/LibreChat). Read-only reference for API and web app parity. Do not modify.
- **`UPSTREAM_VERSION`** — Tracks which official tag/commit this Android build is based on. Updated by the `/sync-upstream` skill.
- **`UPSTREAM_VERSION`** — Tracks which official tag/commit this mobile build is based on. Updated by the `/sync-upstream` skill.
- **`BackendVersion.kt`** (`core/common/`) — `SUPPORTED_BACKEND_VERSION` constant must match the tag in `UPSTREAM_VERSION` (without `v` prefix).
- **`/sync-upstream`** — Claude Code skill to diff upstream releases, identify gaps, propose changes, and implement them with user approval. Uses Agent Teams (investigator, android-expert, implementer, verifier).

View file

@ -14,8 +14,8 @@
```bash
# Clone with submodules (upstream LibreChat server reference)
git clone --recursive https://github.com/anthropics/LibreChat-Android.git
cd LibreChat-Android
git clone --recursive https://github.com/garfiec/Librechat-Mobile.git
cd Librechat-Mobile
```
### Android

View file

@ -1,7 +1,7 @@
# LibreChat Android - Discovery & Context Document
# LibreChat Mobile - Discovery & Context Document
## Project Goal
Build a native Android app (LibreChat-Android) with full feature parity to the LibreChat web application. The app must connect to an existing LibreChat backend server (no backend changes). Users specify the server URL during onboarding.
Build a native mobile app (LibreChat-Mobile) with full feature parity to the LibreChat web application. The app must connect to an existing LibreChat backend server (no backend changes). Users specify the server URL during onboarding.
## Tech Stack (Mandated)
- **UI**: Jetpack Compose + Compose Navigation

View file

@ -1,8 +1,8 @@
# LibreChat Android
# LibreChat Mobile
[![LibreChat](https://img.shields.io/badge/LibreChat-v0.8.4-blue)](https://github.com/danny-avila/LibreChat/releases/tag/v0.8.4)
A third-party native Android client for [LibreChat](https://www.librechat.ai/). Not affiliated with the official LibreChat project — this is an independent app that connects to any self-hosted LibreChat server, no backend modifications required.
A third-party native mobile client for [LibreChat](https://www.librechat.ai/) (Android & iOS). Not affiliated with the official LibreChat project — this is an independent app that connects to any self-hosted LibreChat server, no backend modifications required.
## Features
@ -53,7 +53,7 @@ Add the following to your LibreChat server's `.env` file:
NON_BROWSER_VIOLATION_SCORE=0
```
Without this setting, the server's violation system may accumulate ban points against the Android client if the User-Agent check fails, eventually locking the account out.
Without this setting, the server's violation system may accumulate ban points against the mobile client if the User-Agent check fails, eventually locking the account out.
### Notes

View file

@ -1,4 +1,4 @@
# Upstream LibreChat version this Android build tracks.
# Upstream LibreChat version this mobile build tracks.
# Updated by the sync-upstream skill. Do not edit manually.
tag=v0.8.4
commit=0736ff26686e911c9785a237c63a799db1813f0b

View file

@ -50,7 +50,7 @@ Conversations are integrated into the drawer body. Agents, Files, and Settings a
## Dependencies
This module depends on all `:core:*` and all `:feature:*` modules.
It applies convention plugins: `librechat.android.application`, `librechat.android.compose`, `librechat.android.koin`.
It applies convention plugins: `librechat.mobile.application`, `librechat.mobile.compose`, `librechat.mobile.koin`.
### Server-Synced Favorites
- `NavHostViewModel` exposes `favorites: StateFlow<Set<String>>` and `toggleFavorite()`

View file

@ -1,7 +1,7 @@
plugins {
id("librechat.android.application")
id("librechat.android.compose")
id("librechat.android.koin")
id("librechat.mobile.application")
id("librechat.mobile.compose")
id("librechat.mobile.koin")
}
android {

View file

@ -1,4 +1,4 @@
# LibreChat Android ProGuard Rules
# LibreChat Mobile ProGuard Rules
# Keep Kotlin serialization
-keepattributes *Annotation*, InnerClasses

View file

@ -6,12 +6,12 @@ Convention plugins that apply consistent Gradle configuration across all modules
| Plugin ID | Class | What it does |
|-----------|-------|-------------|
| `librechat.android.application` | `AndroidApplicationConventionPlugin` | AGP application plugin, compileSdk 35, minSdk 26, JDK 17 |
| `librechat.android.library` | `AndroidLibraryConventionPlugin` | AGP library plugin, same SDK/JDK config |
| `librechat.android.compose` | `AndroidComposeConventionPlugin` | Compose compiler, Compose BOM, Material 3 |
| `librechat.android.koin` | `AndroidKoinConventionPlugin` | Koin core + Android dependencies |
| `librechat.android.feature` | `AndroidFeatureConventionPlugin` | Auto-applies: library + compose + koin + serialization |
| `librechat.android.room` | `AndroidRoomConventionPlugin` | Room + KSP, schema export config |
| `librechat.mobile.application` | `AndroidApplicationConventionPlugin` | AGP application plugin, compileSdk 35, minSdk 26, JDK 17 |
| `librechat.mobile.library` | `AndroidLibraryConventionPlugin` | AGP library plugin, same SDK/JDK config |
| `librechat.mobile.compose` | `AndroidComposeConventionPlugin` | Compose compiler, Compose BOM, Material 3 |
| `librechat.mobile.koin` | `AndroidKoinConventionPlugin` | Koin core + Android dependencies |
| `librechat.mobile.feature` | `AndroidFeatureConventionPlugin` | Auto-applies: library + compose + koin + serialization |
| `librechat.mobile.room` | `AndroidRoomConventionPlugin` | Room + KSP, schema export config |
| `librechat.kotlin.serialization` | `KotlinSerializationConventionPlugin` | Kotlinx Serialization plugin + JSON dependency |
## Critical: apply() Signature
@ -32,6 +32,6 @@ build.gradle.kts files. Use `libs.` accessors (e.g., `libs.koin.android`, `libs.
## Feature Module Convention
Feature modules use `id("librechat.android.feature")` which auto-applies library, compose,
Feature modules use `id("librechat.mobile.feature")` which auto-applies library, compose,
koin, and serialization. They only need to add feature-specific dependencies.
Feature modules depend on `:core:*` only, never on other feature modules.

View file

@ -16,27 +16,27 @@ dependencies {
gradlePlugin {
plugins {
register("androidApplication") {
id = "librechat.android.application"
id = "librechat.mobile.application"
implementationClass = "AndroidApplicationConventionPlugin"
}
register("androidLibrary") {
id = "librechat.android.library"
id = "librechat.mobile.library"
implementationClass = "AndroidLibraryConventionPlugin"
}
register("androidCompose") {
id = "librechat.android.compose"
id = "librechat.mobile.compose"
implementationClass = "AndroidComposeConventionPlugin"
}
register("androidFeature") {
id = "librechat.android.feature"
id = "librechat.mobile.feature"
implementationClass = "AndroidFeatureConventionPlugin"
}
register("androidRoom") {
id = "librechat.android.room"
id = "librechat.mobile.room"
implementationClass = "AndroidRoomConventionPlugin"
}
register("androidKoin") {
id = "librechat.android.koin"
id = "librechat.mobile.koin"
implementationClass = "AndroidKoinConventionPlugin"
}
register("kotlinSerialization") {
@ -44,7 +44,7 @@ gradlePlugin {
implementationClass = "KotlinSerializationConventionPlugin"
}
register("androidDetekt") {
id = "librechat.android.detekt"
id = "librechat.mobile.detekt"
implementationClass = "AndroidDetektConventionPlugin"
}
register("kmpLibrary") {

View file

@ -10,7 +10,7 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
with(target) {
pluginManager.apply("com.android.application")
pluginManager.apply("org.jetbrains.kotlin.android")
pluginManager.apply("librechat.android.detekt")
pluginManager.apply("librechat.mobile.detekt")
pluginManager.apply("org.jetbrains.kotlinx.kover")
tasks.withType<KotlinCompile>().configureEach {

View file

@ -5,9 +5,9 @@ import org.gradle.kotlin.dsl.dependencies
class AndroidFeatureConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("librechat.android.library")
pluginManager.apply("librechat.android.compose")
pluginManager.apply("librechat.android.koin")
pluginManager.apply("librechat.mobile.library")
pluginManager.apply("librechat.mobile.compose")
pluginManager.apply("librechat.mobile.koin")
dependencies {
add("implementation", project(":core:ui"))

View file

@ -10,7 +10,7 @@ class AndroidLibraryConventionPlugin : Plugin<Project> {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.android")
pluginManager.apply("librechat.android.detekt")
pluginManager.apply("librechat.mobile.detekt")
pluginManager.apply("org.jetbrains.kotlinx.kover")
tasks.withType<KotlinCompile>().configureEach {

View file

@ -9,7 +9,7 @@ class KmpLibraryConventionPlugin : Plugin<Project> {
with(target) {
pluginManager.apply("com.android.library")
pluginManager.apply("org.jetbrains.kotlin.multiplatform")
pluginManager.apply("librechat.android.detekt")
pluginManager.apply("librechat.mobile.detekt")
pluginManager.apply("org.jetbrains.kotlinx.kover")
extensions.configure<KotlinMultiplatformExtension> {

View file

@ -37,4 +37,4 @@ This lives here (not in `:core:network`) because repositories in `:core:data` ca
- **Pure Kotlin preferred.** Minimal Android dependencies (only what ConnectivityObserver and Koin require).
- **No network or data dependencies.** This module must not depend on `:core:network`, `:core:data`, or `:core:model`.
- Dependencies: `coroutines-core`, `coroutines-android`, Koin.
- Convention plugin: `librechat.android.library` + `librechat.android.koin`.
- Convention plugin: `librechat.mobile.library` + `librechat.mobile.koin`.

View file

@ -55,7 +55,7 @@ Wrap `EncryptedSharedPreferences` in try/catch -- some OEM devices have broken K
## Rules
- Dependencies: `:core:network`, `:core:model`, `:core:common`, Room, DataStore, security-crypto, Ktor, kotlinx-serialization, Timber, Koin.
- Convention plugins: `librechat.android.library` + `librechat.android.koin` + `librechat.android.room` + `librechat.kotlin.serialization`.
- Convention plugins: `librechat.mobile.library` + `librechat.mobile.koin` + `librechat.mobile.room` + `librechat.kotlin.serialization`.
- Repositories must use `safeApiCall` from `:core:common` for all network calls.
- Entities are internal to this module -- feature modules work with domain models from `:core:model`.
- All DAO read methods that the UI observes should return `Flow`, not suspend functions.

View file

@ -27,4 +27,4 @@ All `@Serializable` data classes -- domain models, DTOs, request/response wrappe
- Only dependency beyond `:core:common` is `kotlinx-serialization-json`.
- Use `@Serializable` on every data class. Use `@SerialName` for snake_case JSON fields.
- Use `JsonObject` / `JsonElement` for truly polymorphic/mixed-type fields (e.g., `Agent.avatar`, `Feedback.tag`).
- Convention plugins: `librechat.android.library` + `librechat.kotlin.serialization`.
- Convention plugins: `librechat.mobile.library` + `librechat.kotlin.serialization`.

View file

@ -41,6 +41,6 @@ On reconnection, append `?resume=true` to get a `sync` event with `runSteps[]` +
## Rules
- Dependencies: `:core:model`, `:core:common`, Ktor bundles, kotlinx-serialization, Timber, Koin.
- Convention plugins: `librechat.android.library` + `librechat.android.koin` + `librechat.kotlin.serialization`.
- Convention plugins: `librechat.mobile.library` + `librechat.mobile.koin` + `librechat.kotlin.serialization`.
- API services must not contain business logic -- they are thin HTTP wrappers.
- All `arg`-wrapped endpoints must match the backend pattern: `setBody(mapOf("arg" to mapOf(...)))`.

View file

@ -42,7 +42,7 @@ Material 3 theme and shared Compose components used across all feature modules.
- Components accept domain models from `:core:model` as parameters and render them.
- All components must be stateless or hoist state to the caller.
- Dependencies: `:core:model`, `:core:common`, Coil for image loading, Compose libraries.
- Convention plugins: `librechat.android.library` + `librechat.android.compose`.
- Convention plugins: `librechat.mobile.library` + `librechat.mobile.compose`.
- No DI in this module (no Koin modules, no injected classes).
- Use `@Preview` annotations on all components for Android Studio preview support.
- During SSE streaming, buffer markdown re-renders to ~100ms intervals to avoid frame drops.

View file

@ -3,13 +3,13 @@ package com.garfiec.librechat.core.ui.components
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.painter.Painter
import com.garfiec.librechat.core.model.EModelEndpoint
import librechat_android.core.ui.generated.resources.Res
import librechat_android.core.ui.generated.resources.ic_agents
import librechat_android.core.ui.generated.resources.ic_anthropic
import librechat_android.core.ui.generated.resources.ic_azure
import librechat_android.core.ui.generated.resources.ic_bedrock
import librechat_android.core.ui.generated.resources.ic_google
import librechat_android.core.ui.generated.resources.ic_openai
import librechat_mobile.core.ui.generated.resources.Res
import librechat_mobile.core.ui.generated.resources.ic_agents
import librechat_mobile.core.ui.generated.resources.ic_anthropic
import librechat_mobile.core.ui.generated.resources.ic_azure
import librechat_mobile.core.ui.generated.resources.ic_bedrock
import librechat_mobile.core.ui.generated.resources.ic_google
import librechat_mobile.core.ui.generated.resources.ic_openai
import org.jetbrains.compose.resources.painterResource
/**

View file

@ -23,8 +23,8 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
@Composable
actual fun AgentAvatarPicker(

View file

@ -57,8 +57,8 @@ import com.garfiec.librechat.core.model.ActionAuth
import com.garfiec.librechat.core.model.ActionMetadata
import com.garfiec.librechat.core.model.request.FunctionTool
import com.garfiec.librechat.feature.agents.AgentActionDisplayData
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.util.OpenApiSpecParser
import com.garfiec.librechat.feature.agents.util.ParsedFunctionInfo
import kotlinx.coroutines.delay

View file

@ -29,8 +29,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import kotlin.math.roundToInt
data class AgentAdvancedSettings(

View file

@ -18,8 +18,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
data class AgentCapabilities(
val artifacts: Boolean = false,

View file

@ -16,8 +16,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import com.garfiec.librechat.core.model.AgentCategory
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
/**
* Maps category values to human-readable English labels.

View file

@ -15,8 +15,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
@Composable
fun AgentCodeInterpreterSection(

View file

@ -15,8 +15,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
@Composable
fun AgentFileSearchSection(

View file

@ -38,8 +38,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.feature.agents.AgentHandoffDisplayData
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
@Composable
fun AgentHandoffConfig(

View file

@ -24,8 +24,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.model.mcp.McpTool
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
/** Hierarchical MCP tool picker grouped by server with checkbox selection. */
@Composable

View file

@ -18,8 +18,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
data class ModelOption(
val id: String,

View file

@ -31,8 +31,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
enum class AgentVisibility(val label: String) {
PRIVATE("Private"),

View file

@ -11,8 +11,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
@androidx.compose.runtime.Immutable
data class SupportContactState(

View file

@ -23,8 +23,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
data class AgentVersion(
val version: Int,

View file

@ -49,8 +49,8 @@ import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import coil3.compose.AsyncImage
import com.garfiec.librechat.feature.agents.AgentToolDisplayData
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
/**
* Full-screen dialog for selecting agent tools, matching the web app's ToolSelectDialog.

View file

@ -51,8 +51,8 @@ import com.garfiec.librechat.core.ui.components.AvatarImage
import com.garfiec.librechat.core.ui.components.endpointIconPainter
import com.garfiec.librechat.core.ui.components.ErrorBanner
import com.garfiec.librechat.core.ui.components.LoadingIndicator
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.viewmodel.AgentDetailEvent
import com.garfiec.librechat.feature.agents.viewmodel.AgentDetailViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -53,8 +53,8 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.garfiec.librechat.core.ui.components.ErrorBanner
import com.garfiec.librechat.core.ui.components.LoadingIndicator
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.components.AgentActionsPanel
import com.garfiec.librechat.feature.agents.components.AgentAdvancedPanel
import com.garfiec.librechat.feature.agents.components.AgentAvatarPicker

View file

@ -52,8 +52,8 @@ import com.garfiec.librechat.core.ui.components.ErrorBanner
import com.garfiec.librechat.core.ui.components.LibreChatTopBar
import com.garfiec.librechat.core.ui.components.LoadingIndicator
import com.garfiec.librechat.feature.agents.AgentCardDisplayData
import librechat_android.feature.agents.generated.resources.Res
import librechat_android.feature.agents.generated.resources.*
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.viewmodel.AgentMarketplaceViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -31,8 +31,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.ForgotPasswordViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -31,8 +31,8 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.LoginViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -28,8 +28,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.RegisterViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -31,8 +31,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.ResetPasswordViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -25,8 +25,8 @@ import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.ServerUrlViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -32,8 +32,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.TermsViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -38,8 +38,8 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.TwoFactorViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -30,8 +30,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_android.feature.auth.generated.resources.Res
import librechat_android.feature.auth.generated.resources.*
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.viewmodel.VerifyEmailViewModel
import org.koin.compose.viewmodel.koinViewModel

View file

@ -34,8 +34,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.delay
import co.touchlab.kermit.Logger
import java.io.File

View file

@ -53,8 +53,8 @@ import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.garfiec.librechat.feature.chat.McpServerDisplayData
import com.garfiec.librechat.feature.chat.PromptMentionDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import java.io.File
@Composable

View file

@ -51,8 +51,8 @@ import com.garfiec.librechat.core.common.ToolConstants
import com.garfiec.librechat.core.model.AgentToolCall
import com.garfiec.librechat.core.model.ContentType
import com.garfiec.librechat.core.model.MessageContentPart
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.components.artifact.ArtifactButton
import com.garfiec.librechat.feature.chat.components.artifact.ArtifactPanel
import com.garfiec.librechat.feature.chat.components.artifact.ArtifactSegment

View file

@ -53,8 +53,8 @@ import coil3.compose.AsyncImage
import coil3.SingletonImageLoader
import coil3.request.ImageRequest
import coil3.toBitmap
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

View file

@ -60,8 +60,8 @@ import androidx.compose.ui.unit.isSpecified
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.mikepenz.markdown.m3.Markdown
import com.mikepenz.markdown.m3.markdownColor
import com.mikepenz.markdown.m3.markdownTypography

View file

@ -47,8 +47,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import co.touchlab.kermit.Logger
@Composable

View file

@ -31,8 +31,8 @@ import com.garfiec.librechat.core.model.Message
import com.garfiec.librechat.core.ui.components.AvatarImage
import com.garfiec.librechat.core.ui.components.endpointIconPainter
import com.garfiec.librechat.core.ui.components.isMonochromeEndpointIcon
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.delay
private const val ACTION_AUTO_HIDE_MILLIS = 30_000L

View file

@ -29,8 +29,8 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
actual fun VideoContent(

View file

@ -31,8 +31,8 @@ import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/** Plays video via ExoPlayer (media3) in a 16:9 card. Releases player on dispose via DisposableEffect. */
@Composable

View file

@ -63,8 +63,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.components.CodeBlock
import kotlinx.coroutines.launch
import co.touchlab.kermit.Logger

View file

@ -77,8 +77,8 @@ import com.garfiec.librechat.core.model.ContentType
import com.garfiec.librechat.core.model.MessageContentPart
import com.garfiec.librechat.core.ui.components.LoadingIndicator
import com.garfiec.librechat.core.ui.components.ModelParameterSheet
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.components.ChatInput
import com.garfiec.librechat.feature.chat.components.ComparisonDualPane
import com.garfiec.librechat.feature.chat.components.ComparisonTabBar

View file

@ -27,8 +27,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Data class representing an agent handoff event.

View file

@ -53,8 +53,8 @@ import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import com.garfiec.librechat.core.common.ToolConstants
import com.garfiec.librechat.feature.chat.McpServerDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Floating row of Material 3 chips above the chat input field.

View file

@ -36,8 +36,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.common.ToolConstants
import com.garfiec.librechat.feature.chat.McpServerDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
data class ToolToggle(
val id: String,

View file

@ -55,8 +55,8 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.common.ToolConstants
import com.garfiec.librechat.feature.chat.McpServerDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View file

@ -35,8 +35,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Parsed citation found in message text.

View file

@ -44,8 +44,8 @@ import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.delay
@Composable

View file

@ -42,8 +42,8 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Data class representing a code execution result parsed from tool call output.

View file

@ -42,13 +42,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.feature.chat.McpServerDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.cd_send_message
import librechat_android.feature.chat.generated.resources.cd_start_voice_recording
import librechat_android.feature.chat.generated.resources.cd_stop_generation
import librechat_android.feature.chat.generated.resources.hint_message
import librechat_android.feature.chat.generated.resources.hint_message_model
import librechat_android.feature.chat.generated.resources.recording
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.cd_send_message
import librechat_mobile.feature.chat.generated.resources.cd_start_voice_recording
import librechat_mobile.feature.chat.generated.resources.cd_stop_generation
import librechat_mobile.feature.chat.generated.resources.hint_message
import librechat_mobile.feature.chat.generated.resources.hint_message_model
import librechat_mobile.feature.chat.generated.resources.recording
import org.jetbrains.compose.resources.stringResource
@Immutable

View file

@ -17,8 +17,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Tablet-layout comparison view with two side-by-side panes.

View file

@ -22,8 +22,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.launch
/**

View file

@ -19,8 +19,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Dialog shown when a user taps thumbs-down on an AI message.

View file

@ -31,8 +31,8 @@ import androidx.compose.ui.graphics.vector.ImageVector
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.model.request.ForkOption
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View file

@ -38,8 +38,8 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import coil3.compose.SubcomposeAsyncImage
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
data class ImageGenResult(
val imageUrl: String? = null,

View file

@ -30,8 +30,8 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.filter

View file

@ -24,8 +24,8 @@ import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
fun InlineEditInput(

View file

@ -26,8 +26,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlin.time.Clock
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime

View file

@ -38,8 +38,8 @@ import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
data class LogContent(
val title: String = "Log Output",

View file

@ -28,8 +28,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Data class representing an MCP resource parsed from tool call output.

View file

@ -34,8 +34,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.feature.chat.McpServerDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
fun McpServerSelector(

View file

@ -28,8 +28,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Data class representing a memory artifact parsed from tool call output.

View file

@ -32,8 +32,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.model.Message
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import org.jetbrains.compose.resources.stringResource
/**

View file

@ -47,8 +47,8 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil3.compose.SubcomposeAsyncImage
import com.garfiec.librechat.core.model.FileReference
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Renders files attached to a message. Splits files into images (shown as

View file

@ -39,8 +39,8 @@ import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.common.ChatLayoutConstants
import com.garfiec.librechat.core.model.FeedbackRating
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.util.MessageNode
import com.garfiec.librechat.feature.chat.viewmodel.ActiveToolCall
import com.garfiec.librechat.feature.chat.viewmodel.SearchMatch

View file

@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
fun ModelSelectorButton(

View file

@ -51,8 +51,8 @@ import com.garfiec.librechat.core.model.EndpointConfig
import com.garfiec.librechat.core.ui.components.endpointIconPainter
import com.garfiec.librechat.core.ui.components.isMonochromeEndpointIcon
import com.garfiec.librechat.feature.chat.util.FuzzyMatch
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import org.jetbrains.compose.resources.stringResource
private val IconSize = 20.dp

View file

@ -29,8 +29,8 @@ import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.feature.chat.PresetDisplayData
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View file

@ -17,8 +17,8 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
fun SavePresetDialog(

View file

@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.common.ChatLayoutConstants
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.util.MessageNode
import com.garfiec.librechat.feature.chat.viewmodel.ActiveToolCall

View file

@ -17,8 +17,8 @@ import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
fun SiblingNavigator(

View file

@ -19,8 +19,8 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Blinking cursor indicator shown during streaming. Displays a pipe character (|)

View file

@ -33,8 +33,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.viewmodel.ActiveToolCall
@Composable

View file

@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import org.jetbrains.compose.resources.stringResource
import com.garfiec.librechat.core.common.ToolConstants
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
private data class ToolMenuItem(
val id: String,

View file

@ -36,8 +36,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil3.compose.SubcomposeAsyncImage
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* Data class representing a single web search result parsed from tool call output.

View file

@ -32,8 +32,8 @@ import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
@Composable
fun ArtifactButton(

View file

@ -16,8 +16,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/** Prev/next version navigation for artifacts. [currentIndex] is 0-based; display shows 1-based "v1/N". */
@Composable

View file

@ -31,8 +31,8 @@ import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.ui.components.LibreChatTopBar
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.prompts.components.PromptPreviewPanel
@OptIn(ExperimentalMaterial3Api::class)

View file

@ -35,8 +35,8 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.garfiec.librechat.core.ui.components.LoadingIndicator
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import org.koin.compose.viewmodel.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)

View file

@ -15,8 +15,8 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
private val VARIABLE_REGEX = Regex("\\{\\{\\s*(\\w+)\\s*\\}\\}")

View file

@ -26,8 +26,8 @@ import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.model.Prompt
import librechat_android.feature.chat.generated.resources.Res
import librechat_android.feature.chat.generated.resources.*
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
/**
* A ModalBottomSheet that shows the version history of prompts within a group.

Some files were not shown because too many files have changed in this diff Show more