cleanup: burn down detekt baselines and rename Compose Resources package

This commit is contained in:
Garfie 2026-04-11 12:16:15 -05:00
parent f9148c32c4
commit 63d87c843b
265 changed files with 1207 additions and 1585 deletions

View file

@ -16,7 +16,7 @@
**Key files**:
- `feature/chat/.../components/MessageList.kt` — scroll logic, `pendingFineTuneScroll` flag
- `feature/chat/.../components/MarkdownContent.kt``HighlightedTextSegment` with `onGloballyPositioned`
- `feature/chat/.../components/ContentPartRenderer.kt` — forwards `onFocusedOccurrencePositioned` callback
- `feature/chat/.../components/ContentPartRenderer.kt` — forwards `onFocusedOccurrencePosition` callback
- `feature/chat/.../components/MessageBubble.kt` — forwards callback when `isCurrentSearchMatch`
**What's needed**: On-device debugging session with Logcat/Timber to trace:

View file

@ -1,13 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>LambdaParameterInRestartableEffect:LibreChatNavHost.kt$onDeepLinkConsumed</ID>
<ID>MutableStateAutoboxing:MainActivity.kt$MainActivity$mutableStateOf(0)</ID>
<ID>ParameterNaming:LibreChatNavHost.kt$onDeepLinkConsumed</ID>
<ID>ViewModelForwarding:TabletLayout.kt$MainContent( navigator = navigator, navHostViewModel = navHostViewModel, isInAuthFlow = false, banners = banners, dismissedBannerIds = dismissedBannerIds, onToggleDrawer = { navHostViewModel.setTabletSidebarOpen(!isSidebarOpen) }, )</ID>
<ID>ViewModelForwarding:TabletLayout.kt$MainContent( navigator = navigator, navHostViewModel = navHostViewModel, isInAuthFlow = true, banners = banners, dismissedBannerIds = dismissedBannerIds, onToggleDrawer = {}, modifier = Modifier.fillMaxSize(), )</ID>
<ID>ViewModelForwarding:TabletLayout.kt$MainNavDisplay( navigator = navigator, navHostViewModel = navHostViewModel, onMenuClick = onToggleDrawer, modifier = Modifier.fillMaxSize(), )</ID>
<ID>ViewModelForwarding:TabletLayout.kt$SidebarScaffold( viewModel = navHostViewModel, onNewChat = { if (navigator.currentRoute !is NewChat) { navigator.navigateToTopLevel(NewChat) } }, onConversationClick = { conversationId -&gt; navigator.navigateToChat(conversationId) }, onSettingsClick = { navigator.navigate(SettingsTabbed) }, onSettingsCategorySelected = { category -&gt; navigator.navigate(category.toRoute()) }, onAgentsClick = { navigator.navigate(AgentMarketplace) }, onFilesClick = { navigator.navigate(Files) }, modifier = Modifier .width(SidebarWidth) .fillMaxHeight(), )</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -20,7 +20,7 @@ class AuthFlowTest {
composeTestRule.setContent {
LibreChatTheme {
ServerUrlScreen(
onServerValidated = {},
onServerValidate = {},
)
}
}

View file

@ -24,6 +24,7 @@ import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSiz
import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@ -55,7 +56,7 @@ class MainActivity : ComponentActivity() {
* - Navigates to NewChat if the user is not on a chat screen, or
* - Lets the active ChatViewModel consume the shared content in-place if already on a chat screen.
*/
private var shareNavigationTrigger by mutableStateOf(0)
private var shareNavigationTrigger by mutableIntStateOf(0)
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
@ -108,7 +109,7 @@ class MainActivity : ComponentActivity() {
LibreChatNavHost(
windowSizeClass = windowSizeClass,
deepLinkUri = deepLinkUri,
onDeepLinkConsumed = { deepLinkUri = null },
onDeepLinkConsume = { deepLinkUri = null },
shareNavigationTrigger = shareNavigationTrigger,
modifier = Modifier.weight(1f),
)

View file

@ -5,6 +5,8 @@ import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Modifier
import co.touchlab.kermit.Logger
import com.garfiec.librechat.MainActivity
@ -22,11 +24,12 @@ fun LibreChatNavHost(
modifier: Modifier = Modifier,
windowSizeClass: WindowSizeClass? = null,
deepLinkUri: Uri? = null,
onDeepLinkConsumed: () -> Unit = {},
onDeepLinkConsume: () -> Unit = {},
shareNavigationTrigger: Int = 0,
) {
SharedLibreChatNavHost(modifier = modifier) { navigator, navHostViewModel, mod ->
// Handle deep links
val currentOnDeepLinkConsume by rememberUpdatedState(onDeepLinkConsume)
LaunchedEffect(deepLinkUri) {
deepLinkUri?.let { uri ->
if (uri.scheme == "librechat" && uri.host == "conversation") {
@ -38,7 +41,7 @@ fun LibreChatNavHost(
}
}
}
onDeepLinkConsumed()
currentOnDeepLinkConsume()
}
}
@ -65,7 +68,6 @@ fun LibreChatNavHost(
} else {
PhoneLayout(
navigator = navigator,
navHostViewModel = navHostViewModel,
modifier = mod,
)
}

View file

@ -149,7 +149,6 @@ fun TabletLayout(
// Slot 0: Sidebar -- always 320dp, slides from off-screen left to x=0
Row(modifier = Modifier.fillMaxHeight()) {
SidebarScaffold(
viewModel = navHostViewModel,
onNewChat = {
if (navigator.currentRoute !is NewChat) {
navigator.navigateToTopLevel(NewChat)
@ -161,7 +160,7 @@ fun TabletLayout(
onSettingsClick = {
navigator.navigate(SettingsTabbed)
},
onSettingsCategorySelected = { category ->
onSettingsCategorySelect = { category ->
navigator.navigate(category.toRoute())
},
onAgentsClick = {
@ -181,10 +180,10 @@ fun TabletLayout(
// Slot 1: Main content -- resizes to fill remaining space
MainContent(
navigator = navigator,
navHostViewModel = navHostViewModel,
isInAuthFlow = false,
banners = banners,
dismissedBannerIds = dismissedBannerIds,
onDismissBanner = navHostViewModel::dismissBanner,
onToggleDrawer = {
navHostViewModel.setTabletSidebarOpen(!isSidebarOpen)
},
@ -212,10 +211,10 @@ fun TabletLayout(
// Auth flow -- no sidebar, just main content
MainContent(
navigator = navigator,
navHostViewModel = navHostViewModel,
isInAuthFlow = true,
banners = banners,
dismissedBannerIds = dismissedBannerIds,
onDismissBanner = navHostViewModel::dismissBanner,
onToggleDrawer = {},
modifier = Modifier.fillMaxSize(),
)
@ -225,10 +224,10 @@ fun TabletLayout(
@Composable
private fun MainContent(
navigator: Navigator,
navHostViewModel: NavHostViewModel,
isInAuthFlow: Boolean,
banners: List<Banner>,
dismissedBannerIds: Set<String>,
onDismissBanner: (String) -> Unit,
onToggleDrawer: () -> Unit,
modifier: Modifier = Modifier,
) {
@ -237,13 +236,12 @@ private fun MainContent(
BannerDisplay(
banners = banners,
dismissedIds = dismissedBannerIds,
onDismiss = navHostViewModel::dismissBanner,
onDismiss = onDismissBanner,
modifier = Modifier.padding(top = 4.dp),
)
}
MainNavDisplay(
navigator = navigator,
navHostViewModel = navHostViewModel,
onMenuClick = onToggleDrawer,
modifier = Modifier.fillMaxSize(),
)

View file

@ -2,7 +2,6 @@ package com.garfiec.librechat
import android.app.Application
import android.content.Context
import androidx.lifecycle.SavedStateHandle
import com.garfiec.librechat.core.common.di.commonModule
import com.garfiec.librechat.core.common.network.ConnectivityObserver
import com.garfiec.librechat.core.data.datastore.ConfigCacheDataStore
@ -105,7 +104,6 @@ class KoinGraphVerificationTest {
// Android framework
Context::class,
Application::class,
SavedStateHandle::class,
// core:common provides
CoroutineDispatcher::class,
CoroutineScope::class,

View file

@ -1,8 +1,10 @@
import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.withType
class DetektConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
@ -14,7 +16,10 @@ class DetektConventionPlugin : Plugin<Project> {
buildUponDefaultConfig = true
allRules = false
parallel = true
baseline = file("detekt-baseline.xml")
}
tasks.withType<DetektCreateBaselineTask>().configureEach {
enabled = false
}
dependencies {

View file

@ -3,6 +3,7 @@ import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.getByType
import org.jetbrains.compose.ComposeExtension
import org.jetbrains.compose.resources.ResourcesExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
class KmpComposeConventionPlugin : Plugin<Project> {
@ -11,7 +12,16 @@ class KmpComposeConventionPlugin : Plugin<Project> {
pluginManager.apply("org.jetbrains.compose")
pluginManager.apply("org.jetbrains.kotlin.plugin.compose")
val compose = extensions.getByType<ComposeExtension>().dependencies
val composeExtension = extensions.getByType<ComposeExtension>()
val compose = composeExtension.dependencies
// Override Compose Resources' default package. ResourcesExtension is a
// sub-extension of ComposeExtension — `extensions.configure<ResourcesExtension>`
// at project level throws "extension does not exist".
composeExtension.extensions.configure<ResourcesExtension> {
val modulePath = project.path.removePrefix(":").replace(":", ".")
packageOfResClass = "com.garfiec.librechat.$modulePath.resources"
}
extensions.configure<KotlinMultiplatformExtension> {
sourceSets.commonMain.dependencies {

View file

@ -45,6 +45,8 @@ style:
excludePackageStatements: true
excludeImportStatements: true
excludeRawStrings: true
excludes:
- "**/build/generated/compose/resourceGenerator/**"
ForbiddenComment:
active: false
UnusedPrivateMember:
@ -52,11 +54,14 @@ style:
allowedNames: "(_|ignored|expected|serialVersionUID)"
ignoreAnnotated:
- "Preview"
UnusedPrivateProperty:
active: true
allowedNames: "(_|ignored|expected|serialVersionUID)"
WildcardImport:
active: true
excludeImports:
- "kotlinx.coroutines.flow.*"
- "*.generated.resources.*"
- "*.resources.*"
LoopWithTooManyJumpStatements:
maxJumpCount: 3
ThrowsCount:
@ -70,6 +75,19 @@ naming:
FunctionNaming:
ignoreAnnotated:
- "Composable"
# Detekt's `excludes:` REPLACES (does not merge with) the default value, so
# the default test-source exemptions must be repeated here alongside the
# generated-resources exclude.
excludes:
- "**/test/**"
- "**/androidTest/**"
- "**/commonTest/**"
- "**/jvmTest/**"
- "**/androidUnitTest/**"
- "**/androidInstrumentedTest/**"
- "**/jsTest/**"
- "**/iosTest/**"
- "**/build/generated/compose/resourceGenerator/**"
TopLevelPropertyNaming:
constantPattern: "[A-Z][A-Za-z0-9_]*"
MatchingDeclarationName:
@ -131,3 +149,6 @@ formatting:
active: false
FunctionSignature:
active: false
ImportOrdering:
excludes:
- "**/build/generated/compose/resourceGenerator/**"

View file

@ -1,9 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ImportOrdering:DateExt.kt$import kotlin.time.Clock import kotlin.time.Instant import kotlinx.datetime.LocalDate import kotlinx.datetime.TimeZone import kotlinx.datetime.daysUntil import kotlinx.datetime.toLocalDateTime</ID>
<ID>ImportOrdering:FlowExt.kt$import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.retryWhen import kotlin.time.Clock import kotlin.math.pow</ID>
<ID>NoUnusedImports:CommonModule.kt$com.garfiec.librechat.core.common.di.CommonModule.kt</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,12 +1,10 @@
package com.garfiec.librechat.core.common.di
import com.garfiec.librechat.core.common.network.ConnectivityObserver
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import org.koin.core.module.Module
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
expect val commonPlatformModule: Module

View file

@ -1,11 +1,11 @@
package com.garfiec.librechat.core.common.extensions
import kotlin.time.Clock
import kotlin.time.Instant
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.daysUntil
import kotlinx.datetime.toLocalDateTime
import kotlin.time.Clock
import kotlin.time.Instant
fun String.toInstantOrNull(): Instant? =
try { Instant.parse(this) } catch (_: Exception) { null }

View file

@ -4,8 +4,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.retryWhen
import kotlin.time.Clock
import kotlin.math.pow
import kotlin.time.Clock
fun <T> Flow<T>.retryWithBackoff(
maxRetries: Int = 3,

View file

@ -1,13 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ImportOrdering:ConfigCacheDataStore.kt$import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import com.garfiec.librechat.core.model.EndpointConfig import com.garfiec.librechat.core.model.config.StartupConfig import kotlinx.coroutines.flow.first import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.builtins.MapSerializer import kotlinx.serialization.builtins.serializer import kotlinx.serialization.json.Json import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:ConfigRepositoryImpl.kt$import com.garfiec.librechat.core.common.BackendVersion import com.garfiec.librechat.core.common.result.ApiException import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.common.result.safeApiCall import com.garfiec.librechat.core.data.datastore.ConfigCacheDataStore import com.garfiec.librechat.core.model.EndpointConfig import com.garfiec.librechat.core.model.config.StartupConfig import com.garfiec.librechat.core.network.api.ConfigApi import io.ktor.client.plugins.HttpRequestTimeoutException import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.serialization.SerializationException import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:ConversationMapper.kt$import com.garfiec.librechat.core.data.db.entity.ConversationEntity import com.garfiec.librechat.core.model.Conversation import com.garfiec.librechat.core.model.EModelEndpoint import kotlin.time.Clock import kotlin.time.Instant import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.serializer</ID>
<ID>ImportOrdering:ConversationRepositoryImpl.kt$import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.common.result.safeApiCall import com.garfiec.librechat.core.data.db.dao.ConversationDao import com.garfiec.librechat.core.data.mapper.toEntity import com.garfiec.librechat.core.data.mapper.toModel import com.garfiec.librechat.core.data.mapper.toModels import com.garfiec.librechat.core.model.Conversation import com.garfiec.librechat.core.model.request.ForkConversationRequest import com.garfiec.librechat.core.network.api.ConversationsApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlin.time.Clock import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:MessageMapper.kt$import com.garfiec.librechat.core.data.db.entity.MessageEntity import com.garfiec.librechat.core.model.Attachment import com.garfiec.librechat.core.model.Feedback import com.garfiec.librechat.core.model.FileReference import com.garfiec.librechat.core.model.Message import com.garfiec.librechat.core.model.content.MessageContentPart import kotlin.time.Clock import kotlin.time.Instant import kotlinx.serialization.builtins.ListSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject</ID>
<ID>ImportOrdering:MessageRepositoryImpl.kt$import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.common.result.safeApiCall import com.garfiec.librechat.core.data.db.dao.MessageDao import com.garfiec.librechat.core.data.mapper.toEntity import com.garfiec.librechat.core.data.mapper.toModels import com.garfiec.librechat.core.model.Message import com.garfiec.librechat.core.model.request.BranchMessageRequest import com.garfiec.librechat.core.model.request.UpdateMessageRequest import com.garfiec.librechat.core.network.api.MessagesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import co.touchlab.kermit.Logger</ID>
<ID>NoUnusedImports:DataModule.kt$com.garfiec.librechat.core.data.di.DataModule.kt</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -3,7 +3,6 @@ package com.garfiec.librechat.core.data.di
import android.app.Application
import android.content.Context
import androidx.datastore.core.DataStore
import androidx.lifecycle.SavedStateHandle
import com.garfiec.librechat.core.common.network.ConnectivityObserver
import com.garfiec.librechat.core.network.api.AgentsApi
import com.garfiec.librechat.core.network.api.ApiKeysApi
@ -40,7 +39,6 @@ class DataModuleVerificationTest {
extraTypes = listOf(
Context::class,
Application::class,
SavedStateHandle::class,
Lazy::class,
HttpClient::class,
Json::class,

View file

@ -4,6 +4,7 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.model.EndpointConfig
import com.garfiec.librechat.core.model.config.StartupConfig
import kotlinx.coroutines.flow.first
@ -11,7 +12,6 @@ import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.builtins.MapSerializer
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import co.touchlab.kermit.Logger
class ConfigCacheDataStore(
private val dataStore: DataStore<Preferences>,

View file

@ -3,7 +3,6 @@ package com.garfiec.librechat.core.data.di
import com.garfiec.librechat.core.common.di.KoinQualifiers
import com.garfiec.librechat.core.data.datastore.ConfigCacheDataStore
import com.garfiec.librechat.core.data.datastore.ServerDataStore
import com.garfiec.librechat.core.data.datastore.ServerUrlKeychainFallback
import com.garfiec.librechat.core.data.datastore.SettingsDataStore
import com.garfiec.librechat.core.data.datastore.ThemeDataStore
import com.garfiec.librechat.core.data.db.LibreChatDatabase
@ -41,7 +40,6 @@ import com.garfiec.librechat.core.data.repository.PromptRepository
import com.garfiec.librechat.core.data.repository.PromptRepositoryImpl
import com.garfiec.librechat.core.data.repository.SearchRepository
import com.garfiec.librechat.core.data.repository.SearchRepositoryImpl
import com.garfiec.librechat.core.data.repository.SessionCacheCleaner
import com.garfiec.librechat.core.data.repository.ShareRepository
import com.garfiec.librechat.core.data.repository.ShareRepositoryImpl
import com.garfiec.librechat.core.data.repository.SpeechRepository

View file

@ -3,11 +3,11 @@ package com.garfiec.librechat.core.data.mapper
import com.garfiec.librechat.core.data.db.entity.ConversationEntity
import com.garfiec.librechat.core.model.Conversation
import com.garfiec.librechat.core.model.EModelEndpoint
import kotlin.time.Clock
import kotlin.time.Instant
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import kotlin.time.Clock
import kotlin.time.Instant
private val json = Json { ignoreUnknownKeys = true }

View file

@ -6,11 +6,11 @@ import com.garfiec.librechat.core.model.Feedback
import com.garfiec.librechat.core.model.FileReference
import com.garfiec.librechat.core.model.Message
import com.garfiec.librechat.core.model.content.MessageContentPart
import kotlin.time.Clock
import kotlin.time.Instant
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlin.time.Clock
import kotlin.time.Instant
private val json = Json { ignoreUnknownKeys = true }

View file

@ -1,5 +1,6 @@
package com.garfiec.librechat.core.data.repository
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.common.BackendVersion
import com.garfiec.librechat.core.common.result.ApiException
import com.garfiec.librechat.core.common.result.Result
@ -13,7 +14,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.serialization.SerializationException
import co.touchlab.kermit.Logger
class ConfigRepositoryImpl(
private val configApi: ConfigApi,

View file

@ -1,5 +1,6 @@
package com.garfiec.librechat.core.data.repository
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.common.result.safeApiCall
import com.garfiec.librechat.core.data.db.dao.ConversationDao
@ -12,7 +13,6 @@ import com.garfiec.librechat.core.network.api.ConversationsApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlin.time.Clock
import co.touchlab.kermit.Logger
class ConversationRepositoryImpl(
private val conversationsApi: ConversationsApi,

View file

@ -1,5 +1,6 @@
package com.garfiec.librechat.core.data.repository
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.common.result.safeApiCall
import com.garfiec.librechat.core.data.db.dao.MessageDao
@ -12,7 +13,6 @@ import com.garfiec.librechat.core.network.api.MessagesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import co.touchlab.kermit.Logger
class MessageRepositoryImpl(
private val messagesApi: MessagesApi,

View file

@ -1,5 +1,6 @@
package com.garfiec.librechat.core.data.datastore
import co.touchlab.kermit.Logger
import io.ktor.client.HttpClient
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.ExperimentalForeignApi
@ -9,9 +10,7 @@ import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
import platform.CoreFoundation.CFDictionaryAddValue
import platform.CoreFoundation.CFDictionaryCreateMutable
import platform.CoreFoundation.CFMutableDictionaryRef
import platform.CoreFoundation.CFRelease
import platform.CoreFoundation.CFTypeRef
import platform.CoreFoundation.CFTypeRefVar
import platform.CoreFoundation.kCFBooleanTrue
import platform.Foundation.CFBridgingRelease
@ -35,7 +34,6 @@ import platform.Security.kSecMatchLimit
import platform.Security.kSecMatchLimitOne
import platform.Security.kSecReturnData
import platform.Security.kSecValueData
import co.touchlab.kermit.Logger
@OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)
class IosTokenDataStore(

View file

@ -1,7 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>NoUnusedImports:StartupConfig.kt$com.garfiec.librechat.core.model.config.StartupConfig.kt</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,6 +1,5 @@
package com.garfiec.librechat.core.model.config
import com.garfiec.librechat.core.model.Preset
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonObject

View file

@ -1,13 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ImportOrdering:AgentsApi.kt$import com.garfiec.librechat.core.model.Agent import com.garfiec.librechat.core.model.AgentAction import com.garfiec.librechat.core.model.AgentCategory import com.garfiec.librechat.core.model.AgentExpanded import com.garfiec.librechat.core.model.AgentTool import com.garfiec.librechat.core.model.ToolAuthStatus import com.garfiec.librechat.core.model.ToolCallRecord import com.garfiec.librechat.core.model.ToolCallResult import com.garfiec.librechat.core.model.request.CreateActionRequest import com.garfiec.librechat.core.model.request.CreateAgentRequest import com.garfiec.librechat.core.model.request.RevertAgentRequest import com.garfiec.librechat.core.model.request.ToolCallRequest import com.garfiec.librechat.core.model.request.UpdateAgentRequest import com.garfiec.librechat.core.model.response.AgentListResponse import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.delete import io.ktor.client.request.forms.formData import io.ktor.client.request.forms.submitFormWithBinaryData import io.ktor.client.request.get import io.ktor.client.request.parameter import io.ktor.client.request.patch import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.http.Headers import io.ktor.http.HttpHeaders import io.ktor.http.path import kotlinx.serialization.json.JsonArray import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:AuthApi.kt$import com.garfiec.librechat.core.network.api.dto.LoginResult import com.garfiec.librechat.core.network.api.dto.RefreshResult import com.garfiec.librechat.core.network.api.dto.TwoFactorConfirmRequest import com.garfiec.librechat.core.model.request.LoginRequest import com.garfiec.librechat.core.model.request.OtpVerificationRequest import com.garfiec.librechat.core.model.request.PasswordResetRequest import com.garfiec.librechat.core.model.request.RegisterRequest import com.garfiec.librechat.core.model.request.ResetPasswordRequest import com.garfiec.librechat.core.model.request.TwoFactorDisableRequest import com.garfiec.librechat.core.model.request.TwoFactorVerifyRequest import com.garfiec.librechat.core.model.request.TwoFactorVerifyTempRequest import com.garfiec.librechat.core.model.response.LoginResponse import com.garfiec.librechat.core.model.response.RefreshResponse import com.garfiec.librechat.core.model.response.RegisterResponse import com.garfiec.librechat.core.model.response.TwoFactorSetupResponse import com.garfiec.librechat.core.network.client.CookieHelper import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.header import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.http.path</ID>
<ID>ImportOrdering:LibreChatHttpClient.kt$import co.touchlab.kermit.Logger import io.ktor.client.HttpClient import io.ktor.client.engine.HttpClientEngineFactory import io.ktor.client.plugins.HttpRequestRetry import io.ktor.client.plugins.HttpResponseValidator import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.defaultRequest import io.ktor.client.plugins.logging.LogLevel import io.ktor.client.plugins.logging.Logger as KtorLogger import io.ktor.client.plugins.logging.Logging import io.ktor.client.statement.bodyAsText import io.ktor.http.ContentType import io.ktor.http.HttpHeaders import io.ktor.http.contentType import io.ktor.http.isSuccess import io.ktor.http.takeFrom import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.jsonObject</ID>
<ID>ImportOrdering:SseEventMapper.kt$import com.garfiec.librechat.core.model.Conversation import com.garfiec.librechat.core.model.Message import com.garfiec.librechat.core.model.StreamEvent import com.garfiec.librechat.core.model.content.MessageContentPart import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.booleanOrNull import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:UserApi.kt$import com.garfiec.librechat.core.network.api.dto.UpdateFavoritesRequest import com.garfiec.librechat.core.network.api.dto.UpdatePluginsRequest import com.garfiec.librechat.core.network.api.dto.UserUpdateRequest import com.garfiec.librechat.core.model.User import com.garfiec.librechat.core.model.UserFavorite import com.garfiec.librechat.core.model.request.OtpVerificationRequest import com.garfiec.librechat.core.model.request.ResendVerificationRequest import com.garfiec.librechat.core.model.request.VerifyEmailRequest import com.garfiec.librechat.core.model.response.TermsResponse import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.request.delete import io.ktor.client.request.forms.formData import io.ktor.client.request.forms.submitFormWithBinaryData import io.ktor.client.request.get import io.ktor.client.request.post import io.ktor.client.request.setBody import io.ktor.http.Headers import io.ktor.http.HttpHeaders import io.ktor.http.path</ID>
<ID>MaxLineLength:FilesApi.kt$FilesApi$"uploadFile success: fileId=${response.fileId}, filepath=${response.filepath}, type=${response.type}, width=${response.width}, height=${response.height}"</ID>
<ID>NoEmptyFirstLineInMethodBlock:LibreChatHttpClient.kt$LibreChatHttpClient$ </ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,5 +1,6 @@
package com.garfiec.librechat.core.network.api
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.model.Agent
import com.garfiec.librechat.core.model.AgentAction
import com.garfiec.librechat.core.model.AgentCategory
@ -28,7 +29,6 @@ import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import io.ktor.http.path
import kotlinx.serialization.json.JsonArray
import co.touchlab.kermit.Logger
class AgentsApi constructor(
private val client: HttpClient,

View file

@ -1,8 +1,5 @@
package com.garfiec.librechat.core.network.api
import com.garfiec.librechat.core.network.api.dto.LoginResult
import com.garfiec.librechat.core.network.api.dto.RefreshResult
import com.garfiec.librechat.core.network.api.dto.TwoFactorConfirmRequest
import com.garfiec.librechat.core.model.request.LoginRequest
import com.garfiec.librechat.core.model.request.OtpVerificationRequest
import com.garfiec.librechat.core.model.request.PasswordResetRequest
@ -15,6 +12,9 @@ import com.garfiec.librechat.core.model.response.LoginResponse
import com.garfiec.librechat.core.model.response.RefreshResponse
import com.garfiec.librechat.core.model.response.RegisterResponse
import com.garfiec.librechat.core.model.response.TwoFactorSetupResponse
import com.garfiec.librechat.core.network.api.dto.LoginResult
import com.garfiec.librechat.core.network.api.dto.RefreshResult
import com.garfiec.librechat.core.network.api.dto.TwoFactorConfirmRequest
import com.garfiec.librechat.core.network.client.CookieHelper
import io.ktor.client.HttpClient
import io.ktor.client.call.body

View file

@ -69,7 +69,8 @@ class FilesApi constructor(
}.body()
Logger.d("FilesApi") {
"uploadFile success: fileId=${response.fileId}, filepath=${response.filepath}, type=${response.type}, width=${response.width}, height=${response.height}"
"uploadFile success: fileId=${response.fileId}, filepath=${response.filepath}, " +
"type=${response.type}, width=${response.width}, height=${response.height}"
}
return response
}

View file

@ -1,14 +1,14 @@
package com.garfiec.librechat.core.network.api
import com.garfiec.librechat.core.network.api.dto.UpdateFavoritesRequest
import com.garfiec.librechat.core.network.api.dto.UpdatePluginsRequest
import com.garfiec.librechat.core.network.api.dto.UserUpdateRequest
import com.garfiec.librechat.core.model.User
import com.garfiec.librechat.core.model.UserFavorite
import com.garfiec.librechat.core.model.request.OtpVerificationRequest
import com.garfiec.librechat.core.model.request.ResendVerificationRequest
import com.garfiec.librechat.core.model.request.VerifyEmailRequest
import com.garfiec.librechat.core.model.response.TermsResponse
import com.garfiec.librechat.core.network.api.dto.UpdateFavoritesRequest
import com.garfiec.librechat.core.network.api.dto.UpdatePluginsRequest
import com.garfiec.librechat.core.network.api.dto.UserUpdateRequest
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.delete

View file

@ -9,7 +9,6 @@ import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logger as KtorLogger
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
@ -21,6 +20,7 @@ import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import io.ktor.client.plugins.logging.Logger as KtorLogger
object LibreChatHttpClient {
@ -31,7 +31,6 @@ object LibreChatHttpClient {
serverUrlProvider: ServerUrlProvider,
debug: Boolean = false,
): HttpClient = HttpClient(engineFactory) {
install(ContentNegotiation) {
json(json)
}

View file

@ -1,5 +1,6 @@
package com.garfiec.librechat.core.network.sse
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.model.Conversation
import com.garfiec.librechat.core.model.Message
import com.garfiec.librechat.core.model.StreamEvent
@ -14,7 +15,6 @@ import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import co.touchlab.kermit.Logger
/**
* Maps raw SSE events into domain [StreamEvent]s.

View file

@ -1,27 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ComposableParamOrder:FilterChipBottomSheet.kt$FilterChipBottomSheet</ID>
<ID>FunctionNaming:Drawable0.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainDrawable0Resources(map: MutableMap&lt;String, DrawableResource&gt;)</ID>
<ID>ImportOrdering:Drawable0.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem</ID>
<ID>ImportOrdering:DynamicDropdown.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import org.jetbrains.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp</ID>
<ID>ImportOrdering:DynamicInput.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import org.jetbrains.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp</ID>
<ID>ImportOrdering:DynamicTextarea.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import org.jetbrains.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp</ID>
<ID>ImportOrdering:ExpectResourceCollectors.kt$import kotlin.String import kotlin.collections.Map import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.FontResource import org.jetbrains.compose.resources.PluralStringResource import org.jetbrains.compose.resources.StringArrayResource import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:Res.kt$import kotlin.ByteArray import kotlin.OptIn import kotlin.String import kotlin.Suppress import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.getResourceUri import org.jetbrains.compose.resources.readResourceBytes</ID>
<ID>MaxLineLength:Res.kt$Res$public suspend</ID>
<ID>MultiLineIfElse:DynamicSlider.kt$str + "0".repeat(decimals - existing)</ID>
<ID>MultiLineIfElse:DynamicSlider.kt$str.substring(0, dot + decimals + 1)</ID>
<ID>PackageName:Drawable0.commonMain.kt$package librechat_mobile.core.ui.generated.resources</ID>
<ID>PackageName:ExpectResourceCollectors.kt$package librechat_mobile.core.ui.generated.resources</ID>
<ID>PackageName:Res.kt$package librechat_mobile.core.ui.generated.resources</ID>
<ID>PackageNaming:Drawable0.commonMain.kt$package librechat_mobile.core.ui.generated.resources</ID>
<ID>PackageNaming:ExpectResourceCollectors.kt$package librechat_mobile.core.ui.generated.resources</ID>
<ID>PackageNaming:Res.kt$package librechat_mobile.core.ui.generated.resources</ID>
<ID>ParameterNaming:DynamicParameterPanel.kt$onValueChanged</ID>
<ID>ParameterNaming:DynamicTagsInput.kt$onTagsChanged</ID>
<ID>ParameterNaming:FilterChipBottomSheet.kt$onSelectionChanged</ID>
<ID>ParameterNaming:ModelParameterSheet.kt$onParametersChanged</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -18,8 +18,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.ui.tooling.preview.Preview
/** Labeled dropdown selector using ExposedDropdownMenuBox with read-only text field anchor. */
@OptIn(ExperimentalMaterial3Api::class)

View file

@ -13,8 +13,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.ui.tooling.preview.Preview
/** Single-line text input with label, optional placeholder, and description. */
@Composable

View file

@ -23,7 +23,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
fun DynamicParameterPanel(
definitions: List<ParameterDefinition>,
values: Map<String, String>,
onValueChanged: (key: String, value: String) -> Unit,
onValueChange: (key: String, value: String) -> Unit,
modifier: Modifier = Modifier,
title: String = "Endpoint Parameters",
) {
@ -50,7 +50,7 @@ fun DynamicParameterPanel(
DynamicSlider(
label = definition.label,
value = floatValue,
onValueChange = { onValueChanged(definition.key, it.toString()) },
onValueChange = { onValueChange(definition.key, it.toString()) },
min = min,
max = max,
step = step,
@ -63,7 +63,7 @@ fun DynamicParameterPanel(
label = definition.label,
selectedValue = currentValue,
options = definition.options ?: emptyList(),
onValueChange = { onValueChanged(definition.key, it) },
onValueChange = { onValueChange(definition.key, it) },
description = definition.description,
)
}
@ -73,7 +73,7 @@ fun DynamicParameterPanel(
DynamicCheckbox(
label = definition.label,
checked = checked,
onCheckedChange = { onValueChanged(definition.key, it.toString()) },
onCheckedChange = { onValueChange(definition.key, it.toString()) },
description = definition.description,
)
}
@ -82,7 +82,7 @@ fun DynamicParameterPanel(
DynamicInput(
label = definition.label,
value = currentValue,
onValueChange = { onValueChanged(definition.key, it) },
onValueChange = { onValueChange(definition.key, it) },
placeholder = definition.default,
description = definition.description,
)
@ -93,7 +93,7 @@ fun DynamicParameterPanel(
DynamicCheckbox(
label = definition.label,
checked = checked,
onCheckedChange = { onValueChanged(definition.key, it.toString()) },
onCheckedChange = { onValueChange(definition.key, it.toString()) },
description = definition.description,
)
}
@ -102,7 +102,7 @@ fun DynamicParameterPanel(
DynamicTextarea(
label = definition.label,
value = currentValue,
onValueChange = { onValueChanged(definition.key, it) },
onValueChange = { onValueChange(definition.key, it) },
placeholder = definition.default,
description = definition.description,
)
@ -116,8 +116,8 @@ fun DynamicParameterPanel(
} else {
currentValue.split("\n").filter { it.isNotBlank() }
},
onTagsChanged = { tags ->
onValueChanged(definition.key, tags.joinToString("\n"))
onTagsChange = { tags ->
onValueChange(definition.key, tags.joinToString("\n"))
},
description = definition.description,
maxTags = definition.max?.toInt() ?: 4,
@ -167,7 +167,7 @@ private fun DynamicParameterPanelPreview() {
DynamicParameterPanel(
definitions = definitions,
values = values,
onValueChanged = { key, value ->
onValueChange = { key, value ->
values = values.toMutableMap().apply { this[key] = value }
},
)

View file

@ -99,6 +99,9 @@ private fun roundToDecimals(value: Float, decimals: Int): String {
val dot = str.indexOf('.')
if (dot < 0) return str + "." + "0".repeat(decimals)
val existing = str.length - dot - 1
return if (existing >= decimals) str.substring(0, dot + decimals + 1)
else str + "0".repeat(decimals - existing)
return if (existing >= decimals) {
str.substring(0, dot + decimals + 1)
} else {
str + "0".repeat(decimals - existing)
}
}

View file

@ -34,7 +34,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
fun DynamicTagsInput(
label: String,
tags: List<String>,
onTagsChanged: (List<String>) -> Unit,
onTagsChange: (List<String>) -> Unit,
modifier: Modifier = Modifier,
description: String? = null,
maxTags: Int = 4,
@ -67,7 +67,7 @@ fun DynamicTagsInput(
label = { Text(tag) },
trailingIcon = {
IconButton(
onClick = { onTagsChanged(tags - tag) },
onClick = { onTagsChange(tags - tag) },
modifier = Modifier.size(18.dp),
) {
Icon(
@ -95,7 +95,7 @@ fun DynamicTagsInput(
onDone = {
val trimmed = inputText.trim()
if (trimmed.isNotEmpty() && tags.size < maxTags) {
onTagsChanged(tags + trimmed)
onTagsChange(tags + trimmed)
inputText = ""
}
},
@ -112,7 +112,7 @@ private fun DynamicTagsInputPreview() {
DynamicTagsInput(
label = "Stop Sequences",
tags = tags,
onTagsChanged = { tags = it },
onTagsChange = { tags = it },
description = "Up to 4 sequences where the model will stop generating.",
maxTags = 4,
)

View file

@ -13,8 +13,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.ui.tooling.preview.Preview
/** Multi-line text input with configurable minLines/maxLines, label, and optional description. */
@Composable

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_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 com.garfiec.librechat.core.ui.resources.Res
import com.garfiec.librechat.core.ui.resources.ic_agents
import com.garfiec.librechat.core.ui.resources.ic_anthropic
import com.garfiec.librechat.core.ui.resources.ic_azure
import com.garfiec.librechat.core.ui.resources.ic_bedrock
import com.garfiec.librechat.core.ui.resources.ic_google
import com.garfiec.librechat.core.ui.resources.ic_openai
import org.jetbrains.compose.resources.painterResource
/**

View file

@ -35,7 +35,7 @@ import androidx.compose.ui.unit.dp
*
* @param items All available items to display as chips.
* @param selectedItems Currently selected items.
* @param onSelectionChanged Called with the final selection when the sheet is dismissed.
* @param onSelectionChange Called with the final selection when the sheet is dismissed.
* @param label Extracts a display label from each item.
* @param onDismiss Called when the sheet is dismissed.
* @param title Title displayed at the top of the sheet.
@ -50,15 +50,15 @@ import androidx.compose.ui.unit.dp
fun <T : Any> FilterChipBottomSheet(
items: List<T>,
selectedItems: Set<T>,
onSelectionChanged: (Set<T>) -> Unit,
onSelectionChange: (Set<T>) -> Unit,
label: (T) -> String,
onDismiss: () -> Unit,
title: String,
modifier: Modifier = Modifier,
emptyMessage: String = "",
onAdd: ((String) -> T?)? = null,
addPlaceholder: String = "",
addContentDescription: String = "",
modifier: Modifier = Modifier,
) {
val sheetState = rememberModalBottomSheetState()
var currentSelection by remember { mutableStateOf(selectedItems) }
@ -66,7 +66,7 @@ fun <T : Any> FilterChipBottomSheet(
ModalBottomSheet(
onDismissRequest = {
onSelectionChanged(currentSelection)
onSelectionChange(currentSelection)
onDismiss()
},
modifier = modifier,

View file

@ -104,7 +104,7 @@ data class ModelParameters(
@Composable
fun ModelParameterSheet(
parameters: ModelParameters,
onParametersChanged: (ModelParameters) -> Unit,
onParametersChange: (ModelParameters) -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
selectedEndpoint: String = "",
@ -119,7 +119,7 @@ fun ModelParameterSheet(
) {
ModelParameterContent(
parameters = parameters,
onParametersChanged = onParametersChanged,
onParametersChange = onParametersChange,
selectedEndpoint = selectedEndpoint,
dynamicParameterDefinitions = dynamicParameterDefinitions,
onSaveAsPreset = onSaveAsPreset,
@ -133,7 +133,7 @@ fun ModelParameterSheet(
@Composable
fun ModelParameterContent(
parameters: ModelParameters,
onParametersChanged: (ModelParameters) -> Unit,
onParametersChange: (ModelParameters) -> Unit,
modifier: Modifier = Modifier,
selectedEndpoint: String = "",
dynamicParameterDefinitions: List<ParameterDefinition>? = null,
@ -170,7 +170,7 @@ fun ModelParameterContent(
label = definition.label,
value = currentValue,
onValueChange = { newValue ->
onParametersChanged(parameters.withUpdatedKey(definition.key, newValue))
onParametersChange(parameters.withUpdatedKey(definition.key, newValue))
},
placeholder = definition.default?.ifEmpty { "Default" } ?: "Default",
description = definition.description,
@ -182,7 +182,7 @@ fun ModelParameterContent(
label = definition.label,
value = currentValue,
onValueChange = { newValue ->
onParametersChanged(parameters.withUpdatedKey(definition.key, newValue))
onParametersChange(parameters.withUpdatedKey(definition.key, newValue))
},
placeholder = definition.default?.ifEmpty { null },
description = definition.description,
@ -199,7 +199,7 @@ fun ModelParameterContent(
label = definition.label,
value = floatValue.coerceIn(min, max),
onValueChange = { newValue ->
onParametersChanged(parameters.withUpdatedKey(definition.key, newValue.toString()))
onParametersChange(parameters.withUpdatedKey(definition.key, newValue.toString()))
},
min = min,
max = max,
@ -216,7 +216,7 @@ fun ModelParameterContent(
label = definition.label,
checked = checked,
onCheckedChange = { newChecked ->
onParametersChanged(parameters.withUpdatedKey(definition.key, newChecked.toString()))
onParametersChange(parameters.withUpdatedKey(definition.key, newChecked.toString()))
},
description = definition.description,
)
@ -234,7 +234,7 @@ fun ModelParameterContent(
selectedValue = displayValue,
options = definition.options ?: emptyList(),
onValueChange = { newValue ->
onParametersChanged(parameters.withUpdatedKey(definition.key, newValue))
onParametersChange(parameters.withUpdatedKey(definition.key, newValue))
},
description = definition.description,
)
@ -250,8 +250,8 @@ fun ModelParameterContent(
DynamicTagsInput(
label = definition.label,
tags = tags,
onTagsChanged = { newTags ->
onParametersChanged(
onTagsChange = { newTags ->
onParametersChange(
parameters.withUpdatedKey(definition.key, newTags.joinToString("\n")),
)
},
@ -265,7 +265,7 @@ fun ModelParameterContent(
// Reset to defaults
OutlinedButton(
onClick = {
onParametersChanged(ModelParameters.DEFAULT)
onParametersChange(ModelParameters.DEFAULT)
},
modifier = Modifier.fillMaxWidth(),
) {

View file

@ -1,72 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ComposableParamOrder:AgentDetailScreen.kt$AgentDetailScreen</ID>
<ID>ComposableParamOrder:AgentEditorScreen.kt$AgentEditorScreen</ID>
<ID>DeprecatedKoinApi:AgentsModule.kt$viewModel { params -&gt; AgentDetailViewModel( savedStateHandle = get(), agentRepository = get(), serverDataStore = get(), initialAgentId = params.getOrNull(), ) }</ID>
<ID>DeprecatedKoinApi:AgentsModule.kt$viewModel { params -&gt; AgentEditorViewModel( savedStateHandle = get(), agentRepository = get(), configRepository = get(), mcpRepository = get(), contentReader = get(), initialAgentId = params.getOrNull(), ) }</ID>
<ID>FunctionNaming:String0.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainString0Resources(map: MutableMap&lt;String, StringResource&gt;)</ID>
<ID>FunctionNaming:String1.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainString1Resources(map: MutableMap&lt;String, StringResource&gt;)</ID>
<ID>ImportOrdering:AgentActionsPanel.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Lock import androidx.compose.material.icons.filled.Security import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties 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_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</ID>
<ID>ImportOrdering:AgentAdvancedPanel.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment 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_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.* import kotlin.math.roundToInt import com.garfiec.librechat.feature.agents.components.model.AgentAdvancedSettings</ID>
<ID>ImportOrdering:AgentCapabilitiesSection.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.* import com.garfiec.librechat.feature.agents.components.model.AgentCapabilities</ID>
<ID>ImportOrdering:AgentCategorySelector.kt$import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember 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_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>ImportOrdering:AgentCodeInterpreterSection.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>ImportOrdering:AgentDetailScreen.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.AlertDialog import androidx.compose.material3.AssistChip import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.garfiec.librechat.core.model.EModelEndpoint 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_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 import org.koin.core.parameter.parametersOf</ID>
<ID>ImportOrdering:AgentEditorScreen.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.InputChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource 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_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 import com.garfiec.librechat.feature.agents.components.AgentCategorySelector import com.garfiec.librechat.feature.agents.components.AgentCodeInterpreterSection import com.garfiec.librechat.feature.agents.components.AgentFileSearchSection import com.garfiec.librechat.feature.agents.components.AgentHandoffConfig import com.garfiec.librechat.feature.agents.components.AgentMcpToolsSelector import com.garfiec.librechat.feature.agents.components.AgentModelPicker import com.garfiec.librechat.feature.agents.components.AgentSharingSection import com.garfiec.librechat.feature.agents.components.AgentSupportContactSection import com.garfiec.librechat.feature.agents.components.AgentVersionHistory import com.garfiec.librechat.feature.agents.components.ToolSelectDialog import com.garfiec.librechat.feature.agents.viewmodel.AgentEditorEvent import com.garfiec.librechat.feature.agents.viewmodel.AgentEditorViewModel import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf</ID>
<ID>ImportOrdering:AgentEditorViewModel.kt$import androidx.compose.runtime.Immutable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.garfiec.librechat.feature.agents.util.ContentReader import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.data.repository.AgentRepository import com.garfiec.librechat.core.data.repository.ConfigRepository import com.garfiec.librechat.core.data.repository.McpRepository import com.garfiec.librechat.core.model.ActionMetadata import com.garfiec.librechat.core.model.Agent import com.garfiec.librechat.core.model.AgentAction import com.garfiec.librechat.core.model.AgentCategory import com.garfiec.librechat.core.model.AgentTool import com.garfiec.librechat.core.model.SupportContact import com.garfiec.librechat.core.model.mcp.McpTool import com.garfiec.librechat.core.model.request.CreateActionRequest import com.garfiec.librechat.core.model.request.CreateAgentRequest import com.garfiec.librechat.core.model.request.FunctionTool import com.garfiec.librechat.core.model.request.RevertAgentRequest import com.garfiec.librechat.core.model.request.UpdateAgentRequest import com.garfiec.librechat.feature.agents.AgentActionDisplayData import com.garfiec.librechat.feature.agents.AgentHandoffDisplayData import com.garfiec.librechat.feature.agents.AgentToolDisplayData import com.garfiec.librechat.feature.agents.components.ModelOption import com.garfiec.librechat.feature.agents.components.model.AgentAdvancedSettings import com.garfiec.librechat.feature.agents.components.model.AgentCapabilities import com.garfiec.librechat.feature.agents.components.model.AgentSharingState import com.garfiec.librechat.feature.agents.components.model.AgentVersion import com.garfiec.librechat.feature.agents.components.model.AgentVisibility import com.garfiec.librechat.feature.agents.components.model.SupportContactState import com.garfiec.librechat.feature.agents.util.OpenApiSpecParser import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.floatOrNull import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:AgentFileSearchSection.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>ImportOrdering:AgentHandoffConfig.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.AlertDialog import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.InputChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment 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_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>ImportOrdering:AgentMarketplaceScreen.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.SmartToy import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment 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 androidx.lifecycle.compose.collectAsStateWithLifecycle import com.garfiec.librechat.core.model.EModelEndpoint import com.garfiec.librechat.core.ui.components.AvatarImage import com.garfiec.librechat.core.ui.components.endpointIconPainter import com.garfiec.librechat.core.ui.components.EmptyState 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_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</ID>
<ID>ImportOrdering:AgentMcpToolsSelector.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Checkbox import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment 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_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>ImportOrdering:AgentModelPicker.kt$import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>ImportOrdering:AgentSharingSection.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MenuAnchorType import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.* import com.garfiec.librechat.feature.agents.components.model.AgentSharingState import com.garfiec.librechat.feature.agents.components.model.AgentVisibility</ID>
<ID>ImportOrdering:AgentSupportContactSection.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.* import com.garfiec.librechat.feature.agents.components.model.SupportContactState</ID>
<ID>ImportOrdering:AgentVersionHistory.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.* import com.garfiec.librechat.feature.agents.components.model.AgentVersion</ID>
<ID>ImportOrdering:ExpectResourceCollectors.kt$import kotlin.String import kotlin.collections.Map import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.FontResource import org.jetbrains.compose.resources.PluralStringResource import org.jetbrains.compose.resources.StringArrayResource import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:Res.kt$import kotlin.ByteArray import kotlin.OptIn import kotlin.String import kotlin.Suppress import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.getResourceUri import org.jetbrains.compose.resources.readResourceBytes</ID>
<ID>ImportOrdering:String0.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:String1.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:ToolSelectDialog.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Remove import androidx.compose.material.icons.filled.Search import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp 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_mobile.feature.agents.generated.resources.Res import librechat_mobile.feature.agents.generated.resources.*</ID>
<ID>LambdaParameterInRestartableEffect:AgentDetailScreen.kt$onBack</ID>
<ID>LambdaParameterInRestartableEffect:AgentDetailScreen.kt$onDuplicated</ID>
<ID>LambdaParameterInRestartableEffect:AgentEditorScreen.kt$onDeleted</ID>
<ID>LambdaParameterInRestartableEffect:AgentEditorScreen.kt$onDuplicated</ID>
<ID>LambdaParameterInRestartableEffect:AgentEditorScreen.kt$onSaved</ID>
<ID>LoopWithTooManyJumpStatements:YamlParser.kt$YamlParser$while</ID>
<ID>MaxLineLength:AgentEditorViewModel.kt$AgentEditorViewModel$"name=${agent.name}, description=${agent.description}, instructions=${agent.instructions}, model=${agent.model}, "</ID>
<ID>MaxLineLength:AgentEditorViewModel.kt$AgentEditorViewModel$"name=${newState.name}, description=${newState.description}, instructions=${newState.instructions}, model=${newState.model}, "</ID>
<ID>MaxLineLength:Res.kt$Res$public suspend</ID>
<ID>NoConsecutiveBlankLines:AgentDetailScreen.kt$ </ID>
<ID>NoConsecutiveBlankLines:AgentMarketplaceScreen.kt$ </ID>
<ID>NoUnusedImports:OpenApiSpecParser.kt$com.garfiec.librechat.feature.agents.util.OpenApiSpecParser.kt</ID>
<ID>PackageName:ExpectResourceCollectors.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageName:Res.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageName:String0.commonMain.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageName:String1.commonMain.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageNaming:ExpectResourceCollectors.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageNaming:Res.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageNaming:String0.commonMain.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>PackageNaming:String1.commonMain.kt$package librechat_mobile.feature.agents.generated.resources</ID>
<ID>ParameterNaming:AgentAdvancedPanel.kt$onSettingsChanged</ID>
<ID>ParameterNaming:AgentAvatarPicker.kt$onImageSelected</ID>
<ID>ParameterNaming:AgentCapabilitiesSection.kt$onCapabilitiesChanged</ID>
<ID>ParameterNaming:AgentCategorySelector.kt$onCategorySelected</ID>
<ID>ParameterNaming:AgentDetailScreen.kt$onDuplicated</ID>
<ID>ParameterNaming:AgentEditorScreen.kt$onDeleted</ID>
<ID>ParameterNaming:AgentEditorScreen.kt$onDuplicated</ID>
<ID>ParameterNaming:AgentEditorScreen.kt$onSaved</ID>
<ID>ParameterNaming:AgentMcpToolsSelector.kt$onToolToggled</ID>
<ID>ParameterNaming:AgentModelPicker.kt$onModelSelected</ID>
<ID>ParameterNaming:AgentSharingSection.kt$onSharingChanged</ID>
<ID>ParameterNaming:AgentSupportContactSection.kt$onSupportContactChanged</ID>
<ID>ParameterNaming:ToolSelectDialog.kt$onToolAdded</ID>
<ID>ParameterNaming:ToolSelectDialog.kt$onToolRemoved</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AgentsNavigation.kt$AgentDetail : AgentsRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AgentsNavigation.kt$AgentEditorCreate : AgentsRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AgentsNavigation.kt$AgentEditorEdit : AgentsRoute</ID>
<ID>UnusedPrivateProperty:AgentDetailViewModel.kt$AgentDetailViewModel$savedStateHandle: SavedStateHandle</ID>
<ID>UnusedPrivateProperty:AgentEditorViewModel.kt$AgentEditorViewModel$savedStateHandle: SavedStateHandle</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -18,26 +18,26 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import coil3.compose.AsyncImage
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun AgentAvatarPicker(
avatarUrl: String?,
agentName: String,
onImageSelected: (Any) -> Unit,
onImageSelect: (Any) -> Unit,
modifier: Modifier,
) {
val launcher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent(),
) { uri: Uri? ->
if (uri != null) {
onImageSelected(uri)
onImageSelect(uri)
}
}

View file

@ -2,7 +2,6 @@ package com.garfiec.librechat.feature.agents.di
import android.app.Application
import android.content.Context
import androidx.lifecycle.SavedStateHandle
import com.garfiec.librechat.core.data.datastore.ServerDataStore
import com.garfiec.librechat.core.data.repository.AgentRepository
import com.garfiec.librechat.core.data.repository.ConfigRepository
@ -17,7 +16,6 @@ class AgentsModuleVerificationTest {
extraTypes = listOf(
Context::class,
Application::class,
SavedStateHandle::class,
AgentRepository::class,
ConfigRepository::class,
McpRepository::class,

View file

@ -1,12 +1,12 @@
package com.garfiec.librechat.feature.agents.viewmodel
import com.google.common.truth.Truth.assertThat
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.data.datastore.ServerDataStore
import com.garfiec.librechat.core.data.repository.AgentRepository
import com.garfiec.librechat.core.model.Agent
import com.garfiec.librechat.core.model.AgentCategory
import com.garfiec.librechat.core.model.PaginatedAgents
import com.google.common.truth.Truth.assertThat
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every

View file

@ -45,7 +45,6 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.PasswordVisualTransformation
@ -57,11 +56,12 @@ 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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import com.garfiec.librechat.feature.agents.util.OpenApiSpecParser
import com.garfiec.librechat.feature.agents.util.ParsedFunctionInfo
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.stringResource
private const val VALIDATION_DEBOUNCE_MS = 800L
private const val HIDDEN_PLACEHOLDER = "<HIDDEN>"

View file

@ -26,18 +26,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import kotlin.math.roundToInt
import com.garfiec.librechat.feature.agents.components.model.AgentAdvancedSettings
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
import kotlin.math.roundToInt
@Composable
fun AgentAdvancedPanel(
settings: AgentAdvancedSettings,
onSettingsChanged: (AgentAdvancedSettings) -> Unit,
onSettingsChange: (AgentAdvancedSettings) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
@ -71,7 +71,7 @@ fun AgentAdvancedPanel(
label = stringResource(Res.string.label_temperature),
value = settings.temperature ?: 1.0f,
onValueChange = {
onSettingsChanged(settings.copy(temperature = roundToStep(it, 0.1f)))
onSettingsChange(settings.copy(temperature = roundToStep(it, 0.1f)))
},
valueRange = 0f..2f,
steps = 19,
@ -83,7 +83,7 @@ fun AgentAdvancedPanel(
label = stringResource(Res.string.label_top_p),
value = settings.topP ?: 1.0f,
onValueChange = {
onSettingsChanged(settings.copy(topP = roundToStep(it, 0.05f)))
onSettingsChange(settings.copy(topP = roundToStep(it, 0.05f)))
},
valueRange = 0f..1f,
steps = 19,
@ -106,7 +106,7 @@ fun AgentAdvancedPanel(
value = settings.maxTokens?.toString() ?: "",
onValueChange = { newValue ->
val filtered = newValue.filter { it.isDigit() }
onSettingsChanged(
onSettingsChange(
settings.copy(maxTokens = filtered.toIntOrNull()),
)
},

View file

@ -7,6 +7,6 @@ import androidx.compose.ui.Modifier
expect fun AgentAvatarPicker(
avatarUrl: String?,
agentName: String,
onImageSelected: (Any) -> Unit,
onImageSelect: (Any) -> Unit,
modifier: Modifier = Modifier,
)

View file

@ -15,17 +15,17 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.components.model.AgentCapabilities
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
fun AgentCapabilitiesSection(
capabilities: AgentCapabilities,
onCapabilitiesChanged: (AgentCapabilities) -> Unit,
onCapabilitiesChange: (AgentCapabilities) -> Unit,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier.fillMaxWidth()) {
@ -40,7 +40,7 @@ fun AgentCapabilitiesSection(
description = stringResource(Res.string.artifacts_description),
checked = capabilities.artifacts,
onCheckedChange = {
onCapabilitiesChanged(capabilities.copy(artifacts = it))
onCapabilitiesChange(capabilities.copy(artifacts = it))
},
)
@ -49,7 +49,7 @@ fun AgentCapabilitiesSection(
description = stringResource(Res.string.end_after_tools_description),
checked = capabilities.endAfterTools,
onCheckedChange = {
onCapabilitiesChanged(capabilities.copy(endAfterTools = it))
onCapabilitiesChange(capabilities.copy(endAfterTools = it))
},
)
@ -58,7 +58,7 @@ fun AgentCapabilitiesSection(
description = stringResource(Res.string.hide_sequential_description),
checked = capabilities.hideSequentialOutputs,
onCheckedChange = {
onCapabilitiesChanged(capabilities.copy(hideSequentialOutputs = it))
onCapabilitiesChange(capabilities.copy(hideSequentialOutputs = it))
},
)
@ -80,7 +80,7 @@ fun AgentCapabilitiesSection(
onValueChange = { newValue ->
val filtered = newValue.filter { it.isDigit() }
val intVal = filtered.toIntOrNull() ?: 0
onCapabilitiesChanged(
onCapabilitiesChange(
capabilities.copy(recursionLimit = intVal.coerceIn(1, 100)),
)
},

View file

@ -14,10 +14,10 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
/**
* Maps category values to human-readable English labels.
@ -51,7 +51,7 @@ private fun AgentCategory.displayLabel(): String {
fun AgentCategorySelector(
selectedCategory: String,
categories: List<AgentCategory>,
onCategorySelected: (String) -> Unit,
onCategorySelect: (String) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
@ -90,7 +90,7 @@ fun AgentCategorySelector(
DropdownMenuItem(
text = { Text(category.displayLabel()) },
onClick = {
onCategorySelected(category.value)
onCategorySelect(category.value)
expanded = false
},
)

View file

@ -13,10 +13,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
fun AgentCodeInterpreterSection(

View file

@ -13,10 +13,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
fun AgentFileSearchSection(

View file

@ -35,11 +35,11 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
fun AgentHandoffConfig(

View file

@ -21,18 +21,18 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
/** Hierarchical MCP tool picker grouped by server with checkbox selection. */
@Composable
fun AgentMcpToolsSelector(
mcpTools: List<McpTool>,
selectedToolNames: Set<String>,
onToolToggled: (toolName: String) -> Unit,
onToolToggle: (toolName: String) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
@ -80,7 +80,7 @@ fun AgentMcpToolsSelector(
serverName = serverName,
tools = tools,
selectedToolNames = selectedToolNames,
onToolToggled = onToolToggled,
onToolToggle = onToolToggle,
)
}
}
@ -94,7 +94,7 @@ private fun McpServerToolsGroup(
serverName: String,
tools: List<McpTool>,
selectedToolNames: Set<String>,
onToolToggled: (String) -> Unit,
onToolToggle: (String) -> Unit,
modifier: Modifier = Modifier,
) {
var serverExpanded by remember { mutableStateOf(true) }
@ -125,7 +125,7 @@ private fun McpServerToolsGroup(
McpToolRow(
tool = tool,
isSelected = tool.name in selectedToolNames,
onToggle = { onToolToggled(tool.name) },
onToggle = { onToolToggle(tool.name) },
)
}
}

View file

@ -16,10 +16,10 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
data class ModelOption(
val id: String,
@ -32,7 +32,7 @@ data class ModelOption(
fun AgentModelPicker(
selectedModel: String,
availableModels: List<ModelOption>,
onModelSelected: (modelId: String, provider: String) -> Unit,
onModelSelect: (modelId: String, provider: String) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
@ -107,7 +107,7 @@ fun AgentModelPicker(
DropdownMenuItem(
text = { Text(model.name) },
onClick = {
onModelSelected(model.id, model.endpoint)
onModelSelect(model.id, model.endpoint)
expanded = false
searchQuery = ""
},

View file

@ -29,19 +29,19 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.components.model.AgentSharingState
import com.garfiec.librechat.feature.agents.components.model.AgentVisibility
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
/** Visibility (Private/Team/Public) and collaborative toggle; maps to agent model isPublic/isCollaborative. */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AgentSharingSection(
sharingState: AgentSharingState,
onSharingChanged: (AgentSharingState) -> Unit,
onSharingChange: (AgentSharingState) -> Unit,
modifier: Modifier = Modifier,
) {
var expanded by remember { mutableStateOf(false) }
@ -97,7 +97,7 @@ fun AgentSharingSection(
DropdownMenuItem(
text = { Text(visibility.label) },
onClick = {
onSharingChanged(sharingState.copy(visibility = visibility))
onSharingChange(sharingState.copy(visibility = visibility))
dropdownExpanded = false
},
)
@ -129,7 +129,7 @@ fun AgentSharingSection(
Switch(
checked = sharingState.isCollaborative,
onCheckedChange = {
onSharingChanged(sharingState.copy(isCollaborative = it))
onSharingChange(sharingState.copy(isCollaborative = it))
},
)
}

View file

@ -9,16 +9,16 @@ import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.components.model.SupportContactState
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
fun AgentSupportContactSection(
supportContact: SupportContactState,
onSupportContactChanged: (SupportContactState) -> Unit,
onSupportContactChange: (SupportContactState) -> Unit,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier.fillMaxWidth()) {
@ -37,7 +37,7 @@ fun AgentSupportContactSection(
OutlinedTextField(
value = supportContact.name,
onValueChange = {
onSupportContactChanged(supportContact.copy(name = it))
onSupportContactChange(supportContact.copy(name = it))
},
placeholder = { Text(stringResource(Res.string.support_contact_placeholder)) },
singleLine = true,
@ -55,7 +55,7 @@ fun AgentSupportContactSection(
OutlinedTextField(
value = supportContact.email,
onValueChange = {
onSupportContactChanged(supportContact.copy(email = it))
onSupportContactChange(supportContact.copy(email = it))
},
placeholder = { Text("support@example.com") },
singleLine = true,

View file

@ -21,11 +21,11 @@ import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.components.model.AgentVersion
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
@OptIn(ExperimentalMaterial3Api::class)
@Composable

View file

@ -42,15 +42,15 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import org.jetbrains.compose.resources.stringResource
/**
* Full-screen dialog for selecting agent tools, matching the web app's ToolSelectDialog.
@ -60,8 +60,8 @@ import librechat_mobile.feature.agents.generated.resources.*
fun ToolSelectDialog(
tools: List<AgentToolDisplayData>,
selectedToolIds: List<String>,
onToolAdded: (String) -> Unit,
onToolRemoved: (String) -> Unit,
onToolAdd: (String) -> Unit,
onToolRemove: (String) -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
@ -175,9 +175,9 @@ fun ToolSelectDialog(
isInstalled = isInstalled,
onToggle = {
if (isInstalled) {
onToolRemoved(toolId)
onToolRemove(toolId)
} else {
onToolAdded(toolId)
onToolAdd(toolId)
}
},
)

View file

@ -14,17 +14,22 @@ val agentsModule = module {
includes(agentsPlatformModule)
viewModelOf(::AgentMarketplaceViewModel)
// Koin's constructor-DSL (`viewModelOf`) wires every argument via `get()` and cannot read
// values passed through `parametersOf`. Both VMs below receive `initialAgentId` from the
// navigation layer via `parametersOf`, so the lambda-form `viewModel { params -> ... }` is
// the only DSL that works here. Detekt's `DeprecatedKoinApi` is a blanket stylistic rule,
// not a real `@Deprecated` API, so we suppress it in the narrow places it applies.
@Suppress("DeprecatedKoinApi")
viewModel { params ->
AgentDetailViewModel(
savedStateHandle = get(),
agentRepository = get(),
serverDataStore = get(),
initialAgentId = params.getOrNull(),
)
}
@Suppress("DeprecatedKoinApi")
viewModel { params ->
AgentEditorViewModel(
savedStateHandle = get(),
agentRepository = get(),
configRepository = get(),
mcpRepository = get(),

View file

@ -13,8 +13,11 @@ import kotlinx.serialization.modules.subclass
@Serializable sealed interface AgentsRoute : NavKey
@Serializable data object AgentMarketplace : AgentsRoute
@Serializable data class AgentDetail(val agentId: String) : AgentsRoute
@Serializable data object AgentEditorCreate : AgentsRoute
@Serializable data class AgentEditorEdit(val agentId: String) : AgentsRoute
fun EntryProviderScope<NavKey>.agentsEntries(
@ -34,21 +37,21 @@ fun EntryProviderScope<NavKey>.agentsEntries(
onBack = onBack,
onStartChat = onStartChat,
onEdit = { agentId -> onNavigate(AgentEditorEdit(agentId = agentId)) },
onDuplicated = { agentId -> onNavigate(AgentDetail(agentId = agentId)) },
onDuplicate = { agentId -> onNavigate(AgentDetail(agentId = agentId)) },
agentId = key.agentId,
)
}
entry<AgentEditorCreate> {
AgentEditorScreen(
onBack = onBack,
onSaved = { agentId -> onNavigate(AgentDetail(agentId = agentId)) },
onSave = { agentId -> onNavigate(AgentDetail(agentId = agentId)) },
agentId = null,
)
}
entry<AgentEditorEdit> { key ->
AgentEditorScreen(
onBack = onBack,
onSaved = { agentId -> onNavigate(AgentDetail(agentId = agentId)) },
onSave = { agentId -> onNavigate(AgentDetail(agentId = agentId)) },
agentId = key.agentId,
)
}

View file

@ -39,45 +39,47 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.garfiec.librechat.core.model.EModelEndpoint
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_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.core.ui.components.endpointIconPainter
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import com.garfiec.librechat.feature.agents.viewmodel.AgentDetailEvent
import com.garfiec.librechat.feature.agents.viewmodel.AgentDetailViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
@Composable
fun AgentDetailScreen(
onBack: () -> Unit,
onStartChat: (String) -> Unit,
onEdit: (String) -> Unit,
onDuplicated: (String) -> Unit,
agentId: String? = null,
onDuplicate: (String) -> Unit,
modifier: Modifier = Modifier,
agentId: String? = null,
viewModel: AgentDetailViewModel = koinViewModel { parametersOf(agentId) },
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val currentOnBack by rememberUpdatedState(onBack)
val currentOnDuplicated by rememberUpdatedState(onDuplicate)
LaunchedEffect(Unit) {
viewModel.events.collect { event ->
when (event) {
is AgentDetailEvent.Deleted -> onBack()
is AgentDetailEvent.Duplicated -> onDuplicated(event.agentId)
is AgentDetailEvent.Deleted -> currentOnBack()
is AgentDetailEvent.Duplicated -> currentOnDuplicated(event.agentId)
}
}
}

View file

@ -44,17 +44,15 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
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_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
@ -68,8 +66,11 @@ import com.garfiec.librechat.feature.agents.components.AgentSharingSection
import com.garfiec.librechat.feature.agents.components.AgentSupportContactSection
import com.garfiec.librechat.feature.agents.components.AgentVersionHistory
import com.garfiec.librechat.feature.agents.components.ToolSelectDialog
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import com.garfiec.librechat.feature.agents.viewmodel.AgentEditorEvent
import com.garfiec.librechat.feature.agents.viewmodel.AgentEditorViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
@ -77,22 +78,25 @@ import org.koin.core.parameter.parametersOf
@Composable
fun AgentEditorScreen(
onBack: () -> Unit,
onSaved: (String) -> Unit,
agentId: String? = null,
onSave: (String) -> Unit,
modifier: Modifier = Modifier,
onDeleted: () -> Unit = onBack,
onDuplicated: (String) -> Unit = onSaved,
agentId: String? = null,
onDelete: () -> Unit = onBack,
onDuplicate: (String) -> Unit = onSave,
viewModel: AgentEditorViewModel = koinViewModel { parametersOf(agentId) },
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
var showToolDialog by rememberSaveable { mutableStateOf(false) }
val currentOnSaved by rememberUpdatedState(onSave)
val currentOnDuplicated by rememberUpdatedState(onDuplicate)
val currentOnDeleted by rememberUpdatedState(onDelete)
LaunchedEffect(Unit) {
viewModel.events.collect { event ->
when (event) {
is AgentEditorEvent.SaveSuccess -> onSaved(event.agentId)
is AgentEditorEvent.DuplicateSuccess -> onDuplicated(event.agentId)
is AgentEditorEvent.DeleteSuccess -> onDeleted()
is AgentEditorEvent.SaveSuccess -> currentOnSaved(event.agentId)
is AgentEditorEvent.DuplicateSuccess -> currentOnDuplicated(event.agentId)
is AgentEditorEvent.DeleteSuccess -> currentOnDeleted()
}
}
}
@ -149,8 +153,8 @@ fun AgentEditorScreen(
ToolSelectDialog(
tools = uiState.availableTools,
selectedToolIds = uiState.selectedTools,
onToolAdded = viewModel::onToolAdded,
onToolRemoved = viewModel::onToolRemoved,
onToolAdd = viewModel::onToolAdded,
onToolRemove = viewModel::onToolRemoved,
onDismiss = { showToolDialog = false },
)
}
@ -283,7 +287,7 @@ fun AgentEditorScreen(
AgentAvatarPicker(
avatarUrl = uiState.avatarUrl,
agentName = uiState.name,
onImageSelected = viewModel::uploadAvatar,
onImageSelect = viewModel::uploadAvatar,
)
Spacer(modifier = Modifier.height(4.dp))
Text(
@ -330,7 +334,7 @@ fun AgentEditorScreen(
AgentCategorySelector(
selectedCategory = uiState.category,
categories = uiState.categories,
onCategorySelected = viewModel::onCategoryChanged,
onCategorySelect = viewModel::onCategoryChanged,
modifier = Modifier.fillMaxWidth(),
)
@ -353,7 +357,7 @@ fun AgentEditorScreen(
AgentModelPicker(
selectedModel = uiState.model,
availableModels = uiState.availableModels,
onModelSelected = viewModel::onModelSelected,
onModelSelect = viewModel::onModelSelected,
modifier = Modifier.fillMaxWidth(),
)
@ -389,7 +393,7 @@ fun AgentEditorScreen(
AgentMcpToolsSelector(
mcpTools = uiState.mcpTools,
selectedToolNames = uiState.selectedMcpTools,
onToolToggled = viewModel::onMcpToolToggled,
onToolToggle = viewModel::onMcpToolToggled,
)
Spacer(modifier = Modifier.height(12.dp))
@ -472,7 +476,7 @@ fun AgentEditorScreen(
// 10. Support Contact
AgentSupportContactSection(
supportContact = uiState.supportContact,
onSupportContactChanged = viewModel::onSupportContactChanged,
onSupportContactChange = viewModel::onSupportContactChanged,
)
Spacer(modifier = Modifier.height(16.dp))
@ -533,7 +537,7 @@ fun AgentEditorScreen(
// Sharing & Permissions
AgentSharingSection(
sharingState = uiState.sharingState,
onSharingChanged = viewModel::onSharingChanged,
onSharingChange = viewModel::onSharingChanged,
)
Spacer(modifier = Modifier.height(8.dp))
@ -551,7 +555,7 @@ fun AgentEditorScreen(
// 12. Advanced settings (collapsible)
AgentAdvancedPanel(
settings = uiState.advancedSettings,
onSettingsChanged = viewModel::onAdvancedSettingsChanged,
onSettingsChange = viewModel::onAdvancedSettingsChanged,
)
Spacer(modifier = Modifier.height(24.dp))

View file

@ -41,24 +41,23 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
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 androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.garfiec.librechat.core.model.EModelEndpoint
import com.garfiec.librechat.core.ui.components.AvatarImage
import com.garfiec.librechat.core.ui.components.endpointIconPainter
import com.garfiec.librechat.core.ui.components.EmptyState
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.core.ui.components.endpointIconPainter
import com.garfiec.librechat.feature.agents.AgentCardDisplayData
import librechat_mobile.feature.agents.generated.resources.Res
import librechat_mobile.feature.agents.generated.resources.*
import com.garfiec.librechat.feature.agents.resources.*
import com.garfiec.librechat.feature.agents.resources.Res
import com.garfiec.librechat.feature.agents.viewmodel.AgentMarketplaceViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AgentMarketplaceScreen(

View file

@ -5,7 +5,6 @@ import com.garfiec.librechat.core.model.request.FunctionTool
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonArray

View file

@ -54,19 +54,11 @@ private class YamlParser(input: String) {
val entries = mutableMapOf<String, JsonElement>()
while (pos < lines.size) {
skipBlanksAndComments()
if (pos >= lines.size) break
val indent = currentIndent()
if (indent < baseIndent) break
if (indent > baseIndent) break // belongs to parent
if (shouldStopParsingMap(baseIndent)) break
val line = lines[pos]
val trimmed = line.trimStart()
// Must be a map entry, not a list item
if (trimmed.startsWith("- ")) break
val colonIdx = findKeyColonIndex(trimmed)
if (colonIdx < 0) break
val key = trimmed.substring(0, colonIdx).trim().unquoteYaml()
val afterColon = trimmed.substring(colonIdx + 1).trim()
@ -97,17 +89,59 @@ private class YamlParser(input: String) {
return JsonObject(entries)
}
/**
* Returns true when [parseMap] should stop consuming lines at the current position.
* Extracted from the main loop to reduce jump count preserves the original behaviour
* exactly: end-of-input, dedent to parent, indent into child, list item, or a line
* without a key colon all terminate the map.
*/
private fun shouldStopParsingMap(baseIndent: Int): Boolean {
if (pos >= lines.size) return true
val indent = currentIndent()
if (indent < baseIndent) return true
if (indent > baseIndent) return true // belongs to parent
val trimmed = lines[pos].trimStart()
if (trimmed.startsWith("- ")) return true // must be a map entry, not a list item
if (findKeyColonIndex(trimmed) < 0) return true
return false
}
/**
* Returns true when [parseList] should stop consuming lines at the current position.
* Extracted from the main loop to reduce jump count preserves the original behaviour
* exactly: end-of-input, any indent mismatch, or a line that is not a list item all
* terminate the list.
*/
private fun shouldStopParsingList(baseIndent: Int): Boolean {
if (pos >= lines.size) return true
if (currentIndent() != baseIndent) return true
val trimmed = lines[pos].trimStart()
if (!trimmed.startsWith("- ") && trimmed != "-") return true
return false
}
/**
* Returns true when the continuation loop in [parseListItemMap] should stop at the
* current position. Extracted from the main loop to reduce jump count preserves the
* original behaviour exactly: end-of-input, dedent below the content indent, next list
* item, or a line without a key colon all terminate the continuation.
*/
private fun shouldStopParsingListItemMap(contentIndent: Int): Boolean {
if (pos >= lines.size) return true
if (currentIndent() < contentIndent) return true
val line = lines[pos].trimStart()
if (line.startsWith("- ")) return true // next list item
if (findKeyColonIndex(line) < 0) return true
return false
}
private fun parseList(baseIndent: Int): JsonArray {
val items = mutableListOf<JsonElement>()
while (pos < lines.size) {
skipBlanksAndComments()
if (pos >= lines.size) break
val indent = currentIndent()
if (indent != baseIndent) break
if (shouldStopParsingList(baseIndent)) break
val trimmed = lines[pos].trimStart()
if (!trimmed.startsWith("- ") && trimmed != "-") break
val afterDash = if (trimmed == "-") "" else trimmed.substring(2).trim()
pos++
@ -175,15 +209,11 @@ private class YamlParser(input: String) {
// Continuation lines at contentIndent
while (pos < lines.size) {
skipBlanksAndComments()
if (pos >= lines.size) break
if (shouldStopParsingListItemMap(contentIndent)) break
val indent = currentIndent()
if (indent < contentIndent) break
val line = lines[pos].trimStart()
if (line.startsWith("- ")) break // next list item
val ci = findKeyColonIndex(line)
if (ci < 0) break
val key = line.substring(0, ci).trim().unquoteYaml()
val afterColon = line.substring(ci + 1).trim()

View file

@ -1,7 +1,6 @@
package com.garfiec.librechat.feature.agents.viewmodel
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.garfiec.librechat.core.common.result.Result
@ -33,7 +32,6 @@ sealed interface AgentDetailEvent {
}
class AgentDetailViewModel(
savedStateHandle: SavedStateHandle,
private val agentRepository: AgentRepository,
private val serverDataStore: ServerDataStore,
initialAgentId: String? = null,

View file

@ -1,10 +1,9 @@
package com.garfiec.librechat.feature.agents.viewmodel
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.garfiec.librechat.feature.agents.util.ContentReader
import co.touchlab.kermit.Logger
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.data.repository.AgentRepository
import com.garfiec.librechat.core.data.repository.ConfigRepository
@ -31,6 +30,7 @@ import com.garfiec.librechat.feature.agents.components.model.AgentSharingState
import com.garfiec.librechat.feature.agents.components.model.AgentVersion
import com.garfiec.librechat.feature.agents.components.model.AgentVisibility
import com.garfiec.librechat.feature.agents.components.model.SupportContactState
import com.garfiec.librechat.feature.agents.util.ContentReader
import com.garfiec.librechat.feature.agents.util.OpenApiSpecParser
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@ -46,7 +46,6 @@ import kotlinx.serialization.json.floatOrNull
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import co.touchlab.kermit.Logger
@Immutable
data class AgentEditorUiState(
@ -103,7 +102,6 @@ sealed interface AgentEditorEvent {
}
class AgentEditorViewModel(
savedStateHandle: SavedStateHandle,
private val agentRepository: AgentRepository,
private val configRepository: ConfigRepository,
private val mcpRepository: McpRepository,
@ -171,7 +169,8 @@ class AgentEditorViewModel(
val agent = result.data
Logger.d {
"AgentEditor: Loaded agent fields BEFORE mapping - " +
"name=${agent.name}, description=${agent.description}, instructions=${agent.instructions}, model=${agent.model}, " +
"name=${agent.name}, description=${agent.description}, " +
"instructions=${agent.instructions}, model=${agent.model}, " +
"provider=${agent.provider}, category=${agent.category}, tools=${agent.tools}, " +
"conversationStarters=${agent.conversationStarters}, avatarUrl=${agent.avatarUrl}, " +
"artifacts=${agent.artifacts}, recursionLimit=${agent.recursionLimit}, " +
@ -185,7 +184,8 @@ class AgentEditorViewModel(
.copy(isLoading = false)
Logger.d {
"AgentEditor: UI state AFTER mapping - " +
"name=${newState.name}, description=${newState.description}, instructions=${newState.instructions}, model=${newState.model}, " +
"name=${newState.name}, description=${newState.description}, " +
"instructions=${newState.instructions}, model=${newState.model}, " +
"provider=${newState.provider}, category=${newState.category}, selectedTools=${newState.selectedTools}, " +
"conversationStarters=${newState.conversationStarters}, avatarUrl=${newState.avatarUrl}, " +
"codeInterpreterEnabled=${newState.codeInterpreterEnabled}, fileSearchEnabled=${newState.fileSearchEnabled}, " +

View file

@ -16,7 +16,7 @@ import androidx.compose.ui.unit.dp
actual fun AgentAvatarPicker(
avatarUrl: String?,
agentName: String,
onImageSelected: (Any) -> Unit,
onImageSelect: (Any) -> Unit,
modifier: Modifier,
) {
// iOS: placeholder avatar display only (no image picker yet)

View file

@ -11,7 +11,6 @@ import org.koin.dsl.module
import platform.Foundation.NSData
import platform.Foundation.NSURL
import platform.Foundation.dataWithContentsOfURL
import platform.Foundation.lastPathComponent
import platform.Foundation.pathExtension
import platform.posix.memcpy

View file

@ -1,58 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ComposableParamOrder:ResetPasswordScreen.kt$ResetPasswordScreen</ID>
<ID>ComposableParamOrder:TwoFactorScreen.kt$TwoFactorScreen</ID>
<ID>ComposableParamOrder:VerifyEmailScreen.kt$VerifyEmailScreen</ID>
<ID>DeprecatedKoinApi:AuthModule.kt$viewModel { params -&gt; ResetPasswordViewModel( savedStateHandle = get(), authRepository = get(), initialUserId = params.getOrNull(), initialToken = params.getOrNull(), ) }</ID>
<ID>DeprecatedKoinApi:AuthModule.kt$viewModel { params -&gt; TwoFactorViewModel( savedStateHandle = get(), authRepository = get(), initialTempToken = params.getOrNull(), ) }</ID>
<ID>DeprecatedKoinApi:AuthModule.kt$viewModel { params -&gt; VerifyEmailViewModel( savedStateHandle = get(), userRepository = get(), initialEmail = params.getOrNull(), ) }</ID>
<ID>FunctionNaming:String0.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainString0Resources(map: MutableMap&lt;String, StringResource&gt;)</ID>
<ID>ImportOrdering:ExpectResourceCollectors.kt$import kotlin.String import kotlin.collections.Map import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.FontResource import org.jetbrains.compose.resources.PluralStringResource import org.jetbrains.compose.resources.StringArrayResource import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:ForgotPasswordScreen.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics 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_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</ID>
<ID>ImportOrdering:LoginScreen.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.compose.LifecycleEventEffect import androidx.lifecycle.compose.collectAsStateWithLifecycle 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</ID>
<ID>ImportOrdering:RegisterScreen.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics 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_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</ID>
<ID>ImportOrdering:Res.kt$import kotlin.ByteArray import kotlin.OptIn import kotlin.String import kotlin.Suppress import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.getResourceUri import org.jetbrains.compose.resources.readResourceBytes</ID>
<ID>ImportOrdering:ResetPasswordScreen.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics 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_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 import org.koin.core.parameter.parametersOf</ID>
<ID>ImportOrdering:ServerUrlScreen.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource 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_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</ID>
<ID>ImportOrdering:String0.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:TermsScreen.kt$import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading 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_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</ID>
<ID>ImportOrdering:TwoFactorScreen.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics 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_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 import org.koin.core.parameter.parametersOf</ID>
<ID>ImportOrdering:VerifyEmailScreen.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading 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_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 import org.koin.core.parameter.parametersOf</ID>
<ID>LambdaParameterInRestartableEffect:LoginScreen.kt$onLoginSuccess</ID>
<ID>LambdaParameterInRestartableEffect:LoginScreen.kt$onNavigateToTwoFactor</ID>
<ID>LambdaParameterInRestartableEffect:RegisterScreen.kt$onRegistered</ID>
<ID>LambdaParameterInRestartableEffect:ServerUrlScreen.kt$onServerValidated</ID>
<ID>LambdaParameterInRestartableEffect:TermsScreen.kt$onAccepted</ID>
<ID>LambdaParameterInRestartableEffect:TwoFactorScreen.kt$onVerified</ID>
<ID>LambdaParameterInRestartableEffect:VerifyEmailScreen.kt$onVerified</ID>
<ID>MaxLineLength:Res.kt$Res$public suspend</ID>
<ID>MaxLineLength:TwoFactorScreen.kt$if (uiState.isBackupMode) stringResource(Res.string.use_authenticator_code) else stringResource(Res.string.use_backup_code)</ID>
<ID>ModifierNotUsedAtRoot:ForgotPasswordScreen.kt$modifier = modifier .fillMaxSize() .background(MaterialTheme.colorScheme.background) .padding(padding) .padding(24.dp)</ID>
<ID>NoBlankLineBeforeRbrace:ServerUrlViewModel.kt$ServerUrlViewModel$ </ID>
<ID>PackageName:ExpectResourceCollectors.kt$package librechat_mobile.feature.auth.generated.resources</ID>
<ID>PackageName:Res.kt$package librechat_mobile.feature.auth.generated.resources</ID>
<ID>PackageName:String0.commonMain.kt$package librechat_mobile.feature.auth.generated.resources</ID>
<ID>PackageNaming:ExpectResourceCollectors.kt$package librechat_mobile.feature.auth.generated.resources</ID>
<ID>PackageNaming:Res.kt$package librechat_mobile.feature.auth.generated.resources</ID>
<ID>PackageNaming:String0.commonMain.kt$package librechat_mobile.feature.auth.generated.resources</ID>
<ID>ParameterNaming:RegisterScreen.kt$onRegistered</ID>
<ID>ParameterNaming:ServerUrlScreen.kt$onServerValidated</ID>
<ID>ParameterNaming:TermsScreen.kt$onAccepted</ID>
<ID>ParameterNaming:TwoFactorScreen.kt$onCodeChanged</ID>
<ID>ParameterNaming:TwoFactorScreen.kt$onDigitChanged</ID>
<ID>ParameterNaming:TwoFactorScreen.kt$onVerified</ID>
<ID>ParameterNaming:VerifyEmailScreen.kt$onVerified</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$ForgotPassword : AuthRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$Login : AuthRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$Register : AuthRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$ResetPassword : AuthRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$Terms : AuthRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$TwoFactor : AuthRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:AuthNavigation.kt$VerifyEmail : AuthRoute</ID>
<ID>UnusedPrivateProperty:ResetPasswordViewModel.kt$ResetPasswordViewModel$savedStateHandle: SavedStateHandle</ID>
<ID>UnusedPrivateProperty:TwoFactorViewModel.kt$TwoFactorViewModel$savedStateHandle: SavedStateHandle</ID>
<ID>UnusedPrivateProperty:VerifyEmailViewModel.kt$VerifyEmailViewModel$savedStateHandle: SavedStateHandle</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -2,7 +2,6 @@ package com.garfiec.librechat.feature.auth.di
import android.app.Application
import android.content.Context
import androidx.lifecycle.SavedStateHandle
import com.garfiec.librechat.core.data.datastore.ServerDataStore
import com.garfiec.librechat.core.data.repository.AuthRepository
import com.garfiec.librechat.core.data.repository.ConfigRepository
@ -19,7 +18,6 @@ class AuthModuleVerificationTest {
extraTypes = listOf(
Context::class,
Application::class,
SavedStateHandle::class,
ServerDataStore::class,
AuthRepository::class,
ConfigRepository::class,

View file

@ -1,14 +1,14 @@
package com.garfiec.librechat.feature.auth.viewmodel
import com.google.common.truth.Truth.assertThat
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.data.datastore.ServerDataStore
import com.garfiec.librechat.core.data.repository.AuthRepository
import com.garfiec.librechat.core.data.repository.ConfigRepository
import com.garfiec.librechat.core.model.LoginOutcome
import com.garfiec.librechat.core.model.config.StartupConfig
import com.garfiec.librechat.core.model.User
import com.garfiec.librechat.core.model.config.StartupConfig
import com.garfiec.librechat.feature.auth.oauth.OAuthLauncher
import com.google.common.truth.Truth.assertThat
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk

View file

@ -1,10 +1,10 @@
package com.garfiec.librechat.feature.auth.viewmodel
import com.google.common.truth.Truth.assertThat
import com.garfiec.librechat.core.common.result.Result
import com.garfiec.librechat.core.data.repository.AuthRepository
import com.garfiec.librechat.core.data.repository.ConfigRepository
import com.garfiec.librechat.core.model.config.StartupConfig
import com.google.common.truth.Truth.assertThat
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every

View file

@ -20,25 +20,31 @@ val authModule = module {
viewModelOf(::ServerUrlViewModel)
viewModelOf(::LoginViewModel)
viewModelOf(::RegisterViewModel)
// Koin's constructor-DSL (`viewModelOf`) wires every argument via `get()` and cannot read
// values passed through `parametersOf`. The VMs below receive initial seeds (email, user
// id, token, temp token) from the navigation layer via `parametersOf`, so the lambda-form
// `viewModel { params -> ... }` is the only DSL that works here. Detekt's `DeprecatedKoinApi`
// is a blanket stylistic rule, not a real `@Deprecated` API, so we suppress it in the
// narrow places it applies.
@Suppress("DeprecatedKoinApi")
viewModel { params ->
VerifyEmailViewModel(
savedStateHandle = get(),
userRepository = get(),
initialEmail = params.getOrNull(),
)
}
viewModelOf(::ForgotPasswordViewModel)
@Suppress("DeprecatedKoinApi")
viewModel { params ->
ResetPasswordViewModel(
savedStateHandle = get(),
authRepository = get(),
initialUserId = params.getOrNull(),
initialToken = params.getOrNull(),
)
}
@Suppress("DeprecatedKoinApi")
viewModel { params ->
TwoFactorViewModel(
savedStateHandle = get(),
authRepository = get(),
initialTempToken = params.getOrNull(),
)

View file

@ -18,12 +18,19 @@ import kotlinx.serialization.modules.subclass
@Serializable sealed interface AuthRoute : NavKey
@Serializable data object ServerUrl : AuthRoute
@Serializable data object Login : AuthRoute
@Serializable data object Register : AuthRoute
@Serializable data object ForgotPassword : AuthRoute
@Serializable data class TwoFactor(val tempToken: String) : AuthRoute
@Serializable data class VerifyEmail(val email: String) : AuthRoute
@Serializable data object Terms : AuthRoute
@Serializable data class ResetPassword(val userId: String, val token: String) : AuthRoute
fun EntryProviderScope<NavKey>.authEntries(
@ -33,7 +40,7 @@ fun EntryProviderScope<NavKey>.authEntries(
) {
entry<ServerUrl> {
ServerUrlScreen(
onServerValidated = { onNavigate(Login) },
onServerValidate = { onNavigate(Login) },
)
}
entry<Login> {
@ -48,7 +55,7 @@ fun EntryProviderScope<NavKey>.authEntries(
}
entry<Register> {
RegisterScreen(
onRegistered = onBack,
onRegister = onBack,
onNavigateToLogin = onBack,
)
}
@ -59,21 +66,21 @@ fun EntryProviderScope<NavKey>.authEntries(
}
entry<TwoFactor> { key ->
TwoFactorScreen(
onVerified = onAuthComplete,
onVerify = onAuthComplete,
onBack = onBack,
tempToken = key.tempToken,
)
}
entry<VerifyEmail> { key ->
VerifyEmailScreen(
onVerified = onBack,
onVerify = onBack,
onBack = onBack,
email = key.email,
)
}
entry<Terms> {
TermsScreen(
onAccepted = onAuthComplete,
onAccept = onAuthComplete,
onBack = onBack,
)
}

View file

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
@ -24,16 +25,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.ForgotPasswordViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@ -46,6 +46,7 @@ fun ForgotPasswordScreen(
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
Scaffold(
modifier = modifier,
containerColor = MaterialTheme.colorScheme.background,
topBar = {
TopAppBar(
@ -59,7 +60,7 @@ fun ForgotPasswordScreen(
},
) { padding ->
Column(
modifier = modifier
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(padding)

View file

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.HorizontalDivider
@ -19,21 +20,21 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.LifecycleEventEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import librechat_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.LoginViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
@Composable
@ -46,6 +47,8 @@ fun LoginScreen(
viewModel: LoginViewModel = koinViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val currentOnLoginSuccess by rememberUpdatedState(onLoginSuccess)
val currentOnNavigateToTwoFactor by rememberUpdatedState(onNavigateToTwoFactor)
// Check for OAuth result when returning from Chrome Custom Tab
LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
@ -54,14 +57,14 @@ fun LoginScreen(
LaunchedEffect(uiState.isLoggedIn) {
if (uiState.isLoggedIn) {
onLoginSuccess()
currentOnLoginSuccess()
}
}
LaunchedEffect(uiState.twoFactorTempToken) {
val tempToken = uiState.twoFactorTempToken
if (tempToken != null) {
onNavigateToTwoFactor(tempToken)
currentOnNavigateToTwoFactor(tempToken)
}
}

View file

@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
@ -19,32 +20,33 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.RegisterViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
@Composable
fun RegisterScreen(
onRegistered: () -> Unit,
onRegister: () -> Unit,
onNavigateToLogin: () -> Unit,
modifier: Modifier = Modifier,
viewModel: RegisterViewModel = koinViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val currentOnRegister by rememberUpdatedState(onRegister)
LaunchedEffect(uiState.isRegistered) {
if (uiState.isRegistered) {
onRegistered()
currentOnRegister()
}
}

View file

@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button
@ -24,16 +25,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.ResetPasswordViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
@ -42,9 +42,9 @@ import org.koin.core.parameter.parametersOf
fun ResetPasswordScreen(
onResetComplete: () -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier,
userId: String? = null,
token: String? = null,
modifier: Modifier = Modifier,
viewModel: ResetPasswordViewModel = koinViewModel { parametersOf(userId, token) },
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()

View file

@ -18,29 +18,31 @@ import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.ServerUrlViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
@Composable
fun ServerUrlScreen(
onServerValidated: () -> Unit,
onServerValidate: () -> Unit,
modifier: Modifier = Modifier,
viewModel: ServerUrlViewModel = koinViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val currentOnServerValidated by rememberUpdatedState(onServerValidate)
LaunchedEffect(uiState.isValidated) {
if (uiState.isValidated) {
onServerValidated()
currentOnServerValidated()
}
}

View file

@ -24,34 +24,36 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.TermsViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
/** Displays server terms of service loaded from the backend; gates navigation on user acceptance. */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TermsScreen(
onAccepted: () -> Unit,
onAccept: () -> Unit,
onBack: () -> Unit,
modifier: Modifier = Modifier,
viewModel: TermsViewModel = koinViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val currentOnAccepted by rememberUpdatedState(onAccept)
LaunchedEffect(uiState.isAccepted) {
if (uiState.isAccepted) {
viewModel.consumeAccepted()
onAccepted()
currentOnAccepted()
}
}

View file

@ -27,36 +27,38 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
import androidx.compose.ui.semantics.semantics
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.TwoFactorViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TwoFactorScreen(
onVerified: () -> Unit,
onVerify: () -> Unit,
onBack: () -> Unit,
tempToken: String? = null,
modifier: Modifier = Modifier,
tempToken: String? = null,
viewModel: TwoFactorViewModel = koinViewModel { parametersOf(tempToken) },
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val currentOnVerify by rememberUpdatedState(onVerify)
LaunchedEffect(uiState.isVerified) {
if (uiState.isVerified) onVerified()
if (uiState.isVerified) currentOnVerify()
}
Scaffold(
@ -111,13 +113,13 @@ fun TwoFactorScreen(
if (uiState.isBackupMode) {
BackupCodeInput(
code = uiState.backupCode,
onCodeChanged = viewModel::onBackupCodeChanged,
onCodeChange = viewModel::onBackupCodeChanged,
enabled = !uiState.isLoading,
)
} else {
DigitBoxes(
digits = uiState.digits,
onDigitChanged = viewModel::onDigitChanged,
onDigitChange = viewModel::onDigitChanged,
enabled = !uiState.isLoading,
)
}
@ -155,7 +157,11 @@ fun TwoFactorScreen(
TextButton(onClick = viewModel::toggleBackupMode) {
Text(
if (uiState.isBackupMode) stringResource(Res.string.use_authenticator_code) else stringResource(Res.string.use_backup_code),
if (uiState.isBackupMode) {
stringResource(Res.string.use_authenticator_code)
} else {
stringResource(Res.string.use_backup_code)
},
)
}
}
@ -165,7 +171,7 @@ fun TwoFactorScreen(
@Composable
private fun DigitBoxes(
digits: List<String>,
onDigitChanged: (Int, String) -> Unit,
onDigitChange: (Int, String) -> Unit,
enabled: Boolean,
modifier: Modifier = Modifier,
) {
@ -181,7 +187,7 @@ private fun DigitBoxes(
value = digit,
onValueChange = { value ->
val filtered = value.filter { it.isDigit() }.take(1)
onDigitChanged(index, filtered)
onDigitChange(index, filtered)
if (filtered.isNotEmpty() && index < 5) {
focusRequesters[index + 1].requestFocus()
}
@ -208,13 +214,13 @@ private fun DigitBoxes(
@Composable
private fun BackupCodeInput(
code: String,
onCodeChanged: (String) -> Unit,
onCodeChange: (String) -> Unit,
enabled: Boolean,
modifier: Modifier = Modifier,
) {
OutlinedTextField(
value = code,
onValueChange = onCodeChanged,
onValueChange = onCodeChange,
modifier = modifier.fillMaxWidth(),
enabled = enabled,
singleLine = true,

View file

@ -22,36 +22,38 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.heading
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_mobile.feature.auth.generated.resources.Res
import librechat_mobile.feature.auth.generated.resources.*
import com.garfiec.librechat.feature.auth.resources.*
import com.garfiec.librechat.feature.auth.resources.Res
import com.garfiec.librechat.feature.auth.viewmodel.VerifyEmailViewModel
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
import org.koin.core.parameter.parametersOf
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun VerifyEmailScreen(
onVerified: () -> Unit,
onVerify: () -> Unit,
onBack: () -> Unit,
email: String? = null,
modifier: Modifier = Modifier,
email: String? = null,
viewModel: VerifyEmailViewModel = koinViewModel { parametersOf(email) },
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val email = uiState.email
val displayEmail = uiState.email
val currentOnVerify by rememberUpdatedState(onVerify)
LaunchedEffect(uiState.isVerified) {
if (uiState.isVerified) {
viewModel.consumeVerified()
onVerified()
currentOnVerify()
}
}
@ -94,10 +96,10 @@ fun VerifyEmailScreen(
textAlign = TextAlign.Center,
)
if (email.isNotBlank()) {
if (displayEmail.isNotBlank()) {
Spacer(modifier = Modifier.height(4.dp))
Text(
text = email,
text = displayEmail,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.primary,
textAlign = TextAlign.Center,

View file

@ -1,7 +1,6 @@
package com.garfiec.librechat.feature.auth.viewmodel
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.garfiec.librechat.core.common.result.Result
@ -21,7 +20,6 @@ data class ResetPasswordUiState(
)
class ResetPasswordViewModel(
savedStateHandle: SavedStateHandle,
private val authRepository: AuthRepository,
initialUserId: String? = null,
initialToken: String? = null,

View file

@ -106,5 +106,4 @@ class ServerUrlViewModel(
}
}
}
}

View file

@ -1,7 +1,6 @@
package com.garfiec.librechat.feature.auth.viewmodel
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.garfiec.librechat.core.common.result.Result
@ -22,7 +21,6 @@ data class TwoFactorUiState(
)
class TwoFactorViewModel(
savedStateHandle: SavedStateHandle,
private val authRepository: AuthRepository,
initialTempToken: String? = null,
) : ViewModel() {

View file

@ -1,7 +1,6 @@
package com.garfiec.librechat.feature.auth.viewmodel
import androidx.compose.runtime.Immutable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.garfiec.librechat.core.common.result.Result
@ -24,7 +23,6 @@ data class VerifyEmailUiState(
)
class VerifyEmailViewModel(
savedStateHandle: SavedStateHandle,
private val userRepository: UserRepository,
initialEmail: String? = null,
) : ViewModel() {

View file

@ -56,6 +56,7 @@ class IosOAuthLauncher : OAuthLauncher {
override fun extractTokenFromCookies(serverUrl: String): String? {
val url = NSURL.URLWithString(serverUrl) ?: return null
val cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage.cookiesForURL(url) ?: return null
@Suppress("UNCHECKED_CAST")
val cookieList = cookies as List<NSHTTPCookie>
return cookieList.firstOrNull { it.name == "refreshToken" }?.value
@ -64,6 +65,7 @@ class IosOAuthLauncher : OAuthLauncher {
override fun clearOAuthCookie(serverUrl: String) {
val url = NSURL.URLWithString(serverUrl) ?: return
val cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage.cookiesForURL(url) ?: return
@Suppress("UNCHECKED_CAST")
val cookieList = cookies as List<NSHTTPCookie>
cookieList.filter { it.name == "refreshToken" }.forEach {

View file

@ -1,118 +0,0 @@
<?xml version="1.0" ?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>ComposableParamOrder:CommonChatInputCore.kt$CommonChatInputCore</ID>
<ID>DeprecatedKoinApi:ChatModule.kt$viewModel { params -&gt; PromptEditorViewModel( promptRepository = get(), savedStateHandle = get(), initialGroupId = params.getOrNull(), ) }</ID>
<ID>FunctionNaming:String0.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainString0Resources(map: MutableMap&lt;String, StringResource&gt;)</ID>
<ID>FunctionNaming:String1.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainString1Resources(map: MutableMap&lt;String, StringResource&gt;)</ID>
<ID>FunctionNaming:String2.commonMain.kt$@InternalResourceApi internal fun _collectCommonMainString2Resources(map: MutableMap&lt;String, StringResource&gt;)</ID>
<ID>ImportOrdering:AgentHandoffCard.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.SmartToy import androidx.compose.material.icons.filled.SwapHoriz import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ArtifactButton.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Article import androidx.compose.material.icons.automirrored.filled.OpenInNew import androidx.compose.material.icons.filled.AccountTree import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.Image import androidx.compose.material.icons.filled.Web import androidx.compose.material.icons.filled.Widgets import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ArtifactVersionNav.kt$import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:AttachmentChipsRow.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.InsertDriveFile import androidx.compose.material.icons.filled.AttachFile import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.Extension import androidx.compose.material.icons.filled.FindInPage import androidx.compose.material.icons.filled.TravelExplore import androidx.compose.material3.AssistChip import androidx.compose.material3.AssistChipDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import com.garfiec.librechat.core.common.ToolConstants import com.garfiec.librechat.feature.chat.model.McpServerDisplayData import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ChatInputToolbar.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.FindInPage import androidx.compose.material.icons.filled.MoreHoriz import androidx.compose.material.icons.filled.Search import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription 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.model.McpServerDisplayData import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ChatToolsBottomSheet.kt$import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material.icons.filled.AttachFile import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.Extension import androidx.compose.material.icons.filled.FindInPage import androidx.compose.material.icons.filled.PhotoCamera import androidx.compose.material.icons.filled.PhotoLibrary import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.SmartToy import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics 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.model.McpServerDisplayData import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ChatViewModel.kt$import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.garfiec.librechat.core.common.EndpointConstants import com.garfiec.librechat.core.common.ToolConstants import com.garfiec.librechat.core.common.network.ConnectivityObserver import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.common.result.getOrNull import com.garfiec.librechat.core.data.datastore.LatexRenderer import com.garfiec.librechat.core.data.datastore.ServerDataStore import com.garfiec.librechat.core.data.datastore.SettingsDataStore import com.garfiec.librechat.core.data.repository.AgentRepository import com.garfiec.librechat.core.data.repository.ChatRepository import com.garfiec.librechat.core.data.repository.ConfigRepository import com.garfiec.librechat.core.data.repository.ConversationRepository import com.garfiec.librechat.core.data.repository.DraftRepository import com.garfiec.librechat.core.data.repository.McpRepository import com.garfiec.librechat.core.data.repository.MessageRepository import com.garfiec.librechat.core.data.repository.PresetRepository import com.garfiec.librechat.core.data.repository.PromptRepository import com.garfiec.librechat.core.data.repository.ShareRepository import com.garfiec.librechat.core.data.repository.UserRepository import com.garfiec.librechat.core.model.Attachment import com.garfiec.librechat.core.model.EModelEndpoint import com.garfiec.librechat.core.model.Message import com.garfiec.librechat.core.model.Preset import com.garfiec.librechat.core.model.StreamEvent import com.garfiec.librechat.core.model.request.EphemeralAgent import com.garfiec.librechat.core.ui.components.ModelParameters import com.garfiec.librechat.feature.chat.model.PresetDisplayData import com.garfiec.librechat.feature.chat.model.PromptMentionDisplayData import com.garfiec.librechat.feature.chat.components.AttachedFile import com.garfiec.librechat.feature.chat.util.NEW_CHAT_DRAFT_KEY import com.garfiec.librechat.feature.chat.util.buildActiveMessagePath import com.garfiec.librechat.feature.chat.viewmodel.delegate.ConversationActionsDelegate import com.garfiec.librechat.feature.chat.viewmodel.delegate.InConversationSearchDelegate import com.garfiec.librechat.feature.chat.viewmodel.delegate.ModelSelectionDelegate import com.garfiec.librechat.feature.chat.viewmodel.delegate.PlatformDelegateFactory import com.garfiec.librechat.feature.chat.viewmodel.delegate.PresetPromptDelegate import com.garfiec.librechat.feature.chat.viewmodel.delegate.toSerialName import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Job import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import co.touchlab.kermit.Logger import kotlin.time.Clock import kotlin.uuid.ExperimentalUuidApi import kotlin.uuid.Uuid</ID>
<ID>ImportOrdering:CitationChip.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.ClickableText import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.OpenInNew import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalUriHandler import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.withStyle 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:CodeBlock.kt$import androidx.compose.animation.AnimatedContent import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import com.garfiec.librechat.feature.chat.util.copyToClipboard import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontFamily 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import kotlinx.coroutines.delay</ID>
<ID>ImportOrdering:CodeExecutionCard.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Cancel import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SuggestionChip import androidx.compose.material3.SuggestionChipDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ComparisonDualPane.kt$import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ComparisonTabBar.kt$import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import kotlinx.coroutines.launch</ID>
<ID>ImportOrdering:ExpectResourceCollectors.kt$import kotlin.String import kotlin.collections.Map import org.jetbrains.compose.resources.DrawableResource import org.jetbrains.compose.resources.FontResource import org.jetbrains.compose.resources.PluralStringResource import org.jetbrains.compose.resources.StringArrayResource import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:FeedbackCommentDialog.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.AlertDialog import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ForkOptionsBottomSheet.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.CallSplit import androidx.compose.material.icons.filled.AccountTree import androidx.compose.material.icons.filled.LinearScale import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ImageGenCard.kt$import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BrokenImage import androidx.compose.material.icons.filled.Image import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import coil3.compose.SubcomposeAsyncImage import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:InConvoSearchBar.kt$import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalSoftwareKeyboardController import org.jetbrains.compose.resources.stringResource 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_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</ID>
<ID>ImportOrdering:InlineEditInput.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:LandingContent.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ChatBubbleOutline import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.heading 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import kotlin.time.Clock import kotlinx.datetime.DayOfWeek import kotlinx.datetime.TimeZone import kotlinx.datetime.toLocalDateTime</ID>
<ID>ImportOrdering:LogContentCard.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Terminal import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:McpResourceCarousel.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Extension import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:McpServerSelector.kt$import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Hub import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenu import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription 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.model.McpServerDisplayData import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:MemoryArtifactCard.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SuggestionChip import androidx.compose.material3.SuggestionChipDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:MessageContentAndActions.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.CallSplit import androidx.compose.material.icons.automirrored.filled.VolumeUp import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Stop import androidx.compose.material.icons.filled.ThumbDown import androidx.compose.material.icons.filled.ThumbUp import androidx.compose.material.icons.outlined.ThumbDown import androidx.compose.material.icons.outlined.ThumbUp import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import org.jetbrains.compose.resources.stringResource</ID>
<ID>ImportOrdering:MessageFiles.kt$import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.InsertDriveFile import androidx.compose.material.icons.filled.AttachFile import androidx.compose.material.icons.filled.BrokenImage import androidx.compose.material.icons.filled.Description import androidx.compose.material.icons.filled.PictureAsPdf import androidx.compose.material.icons.filled.TableChart import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:MessageList.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowDown import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButtonDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SmallFloatingActionButton import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberUpdatedState import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.boundsInRoot import androidx.compose.ui.platform.LocalDensity 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_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 import kotlinx.coroutines.delay import kotlinx.coroutines.launch</ID>
<ID>ImportOrdering:ModelSelectionDelegate.kt$import com.garfiec.librechat.core.common.EndpointConstants import com.garfiec.librechat.core.common.ToolConstants import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.data.datastore.SettingsDataStore import com.garfiec.librechat.core.data.repository.AgentRepository import com.garfiec.librechat.core.data.repository.ConfigRepository import com.garfiec.librechat.core.data.repository.McpRepository import com.garfiec.librechat.core.model.EModelEndpoint import com.garfiec.librechat.core.model.mcp.McpServer import com.garfiec.librechat.core.model.request.AddedConversation import com.garfiec.librechat.core.ui.components.ModelParameters import com.garfiec.librechat.feature.chat.model.McpServerDisplayData import com.garfiec.librechat.feature.chat.viewmodel.ChatScreenState import com.garfiec.librechat.feature.chat.viewmodel.ChatStateHandle import com.garfiec.librechat.feature.chat.viewmodel.ComparisonState import kotlinx.coroutines.launch import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:ModelSelectorButton.kt$import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:ModelSelectorSheet.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.outlined.AutoAwesome import androidx.compose.material.icons.outlined.Create import androidx.compose.material.icons.outlined.Public import androidx.compose.material.icons.outlined.SmartToy import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp import coil3.compose.AsyncImage import com.garfiec.librechat.core.common.EndpointConstants import com.garfiec.librechat.core.model.Agent 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import org.jetbrains.compose.resources.stringResource</ID>
<ID>ImportOrdering:PlatformFileHandler.kt$import com.garfiec.librechat.feature.chat.components.AttachedFile import com.garfiec.librechat.core.model.FileReference import kotlinx.coroutines.Job import kotlinx.coroutines.flow.StateFlow</ID>
<ID>ImportOrdering:PresetPicker.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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 com.garfiec.librechat.feature.chat.model.PresetDisplayData import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:PresetPromptDelegate.kt$import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.data.repository.PresetRepository import com.garfiec.librechat.core.data.repository.PromptRepository import com.garfiec.librechat.core.model.EModelEndpoint import com.garfiec.librechat.core.model.Preset import com.garfiec.librechat.core.model.PromptGroup import com.garfiec.librechat.feature.chat.model.PresetDisplayData import com.garfiec.librechat.feature.chat.model.PromptMentionDisplayData import com.garfiec.librechat.feature.chat.viewmodel.ChatStateHandle import kotlinx.coroutines.launch import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:PromptDetailScreen.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.outlined.Description import androidx.compose.material3.Button import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import com.garfiec.librechat.feature.chat.prompts.components.PromptPreviewPanel</ID>
<ID>ImportOrdering:PromptEditorScreen.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.History import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember 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 androidx.lifecycle.compose.collectAsStateWithLifecycle import com.garfiec.librechat.core.ui.components.LoadingIndicator import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import org.koin.compose.viewmodel.koinViewModel import org.koin.core.parameter.parametersOf</ID>
<ID>ImportOrdering:PromptFilterSheet.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SheetState import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:PromptPreviewPanel.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:PromptShareDialog.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material3.AlertDialog import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.RadioButton import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.garfiec.librechat.feature.chat.util.copyToClipboard import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:PromptVariablesSection.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:PromptVersionsSheet.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material3.Badge import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment 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 com.garfiec.librechat.core.model.Prompt import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:PromptsLibraryScreen.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.FilterList import androidx.compose.material.icons.filled.Share import androidx.compose.material.icons.outlined.Description import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment 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 androidx.lifecycle.compose.collectAsStateWithLifecycle import com.garfiec.librechat.core.ui.components.EmptyState 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 librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import com.garfiec.librechat.feature.chat.prompts.components.PromptFilterSheet import com.garfiec.librechat.feature.chat.prompts.components.PromptShareDialog import com.garfiec.librechat.feature.chat.prompts.components.PromptSortOrder import com.garfiec.librechat.feature.chat.prompts.components.VariableInputDialog import org.koin.compose.viewmodel.koinViewModel</ID>
<ID>ImportOrdering:PromptsViewModel.kt$import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.garfiec.librechat.core.common.result.Result import com.garfiec.librechat.core.data.repository.PromptRepository import com.garfiec.librechat.core.model.PromptGroup import com.garfiec.librechat.core.model.request.AddPromptToGroupRequest import com.garfiec.librechat.core.model.request.CreatePromptData import com.garfiec.librechat.core.model.request.CreatePromptGroupData import com.garfiec.librechat.core.model.request.CreatePromptRequest import com.garfiec.librechat.core.model.request.UpdatePromptGroupRequest import com.garfiec.librechat.core.model.request.UpdatePromptTagRequest import com.garfiec.librechat.feature.chat.prompts.components.PromptSortOrder import com.garfiec.librechat.feature.chat.prompts.components.extractVariables import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:Res.kt$import kotlin.ByteArray import kotlin.OptIn import kotlin.String import kotlin.Suppress import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.getResourceUri import org.jetbrains.compose.resources.readResourceBytes</ID>
<ID>ImportOrdering:SavePresetDialog.kt$import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.material3.AlertDialog import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:SecondaryMessageList.kt$import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment 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_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</ID>
<ID>ImportOrdering:SharedContentParts.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.BrokenImage import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material.icons.filled.Psychology import androidx.compose.material.icons.filled.Videocam import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import coil3.compose.SubcomposeAsyncImage import com.garfiec.librechat.core.common.ToolConstants import com.garfiec.librechat.core.model.Attachment import com.garfiec.librechat.core.model.ContentType import com.garfiec.librechat.core.model.content.MessageContentPart import com.garfiec.librechat.feature.chat.components.artifact.Artifact 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 import com.garfiec.librechat.feature.chat.components.artifact.detectArtifacts import com.garfiec.librechat.feature.chat.components.artifact.groupArtifactVersions import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import org.jetbrains.compose.resources.stringResource</ID>
<ID>ImportOrdering:SiblingNavigator.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.KeyboardArrowLeft import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:StreamingIndicator.kt$import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.infiniteRepeatable import androidx.compose.animation.core.rememberInfiniteTransition import androidx.compose.animation.core.tween import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.contentDescription 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:StreamingToolCallCard.kt$import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.ExpandLess import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.* import com.garfiec.librechat.feature.chat.viewmodel.ActiveToolCall</ID>
<ID>ImportOrdering:String0.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:String1.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:String2.commonMain.kt$import kotlin.OptIn import kotlin.String import kotlin.collections.MutableMap import org.jetbrains.compose.resources.InternalResourceApi import org.jetbrains.compose.resources.ResourceContentHash import org.jetbrains.compose.resources.ResourceItem import org.jetbrains.compose.resources.StringResource</ID>
<ID>ImportOrdering:ToolCallParsing.kt$import com.garfiec.librechat.core.model.content.AgentToolCall import com.garfiec.librechat.core.model.Attachment import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.intOrNull import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import co.touchlab.kermit.Logger</ID>
<ID>ImportOrdering:ToolsDropdownMenu.kt$import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Code import androidx.compose.material.icons.filled.Image import androidx.compose.material.icons.filled.Search import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.remember 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_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:VariableInputDialog.kt$import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.unit.dp import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>ImportOrdering:WebSearchResultCard.kt$import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Language import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalUriHandler import org.jetbrains.compose.resources.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.role 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 coil3.compose.SubcomposeAsyncImage import librechat_mobile.feature.chat.generated.resources.Res import librechat_mobile.feature.chat.generated.resources.*</ID>
<ID>LambdaParameterInRestartableEffect:MessageList.kt$onSearchScrollHandled</ID>
<ID>LambdaParameterInRestartableEffect:PromptEditorScreen.kt$onBack</ID>
<ID>MaxLineLength:ChatViewModel.kt$ChatViewModel$Logger.d { "sendMessage: webSearch=$webSearchEnabled, endpoint=${_uiState.value.selectedEndpoint}, model=${_uiState.value.selectedModel}, files=${fileRefs.size}, ephemeralAgent=$ephemeralAgent" }</ID>
<ID>MaxLineLength:ModelSelectorSheet.kt$contentDescription = stringResource(if (isExpanded) Res.string.cd_collapse_section else Res.string.cd_expand_section, displayLabel)</ID>
<ID>MaxLineLength:Res.kt$Res$public suspend</ID>
<ID>MaxLineLength:SharedContentParts.kt$Icon(Icons.Default.BrokenImage, stringResource(Res.string.cd_failed_to_load_image), Modifier.size(32.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Icon(Icons.Default.Build, stringResource(Res.string.cd_tool_call), Modifier.size(16.dp), tint = MaterialTheme.colorScheme.onSurfaceVariant)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Icon(Icons.Default.Psychology, stringResource(Res.string.cd_thinking_indicator), Modifier.size(18.dp), tint = MaterialTheme.colorScheme.primary)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Icon(Icons.Filled.Videocam, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.size(18.dp))</ID>
<ID>MaxLineLength:SharedContentParts.kt$MarkdownContent(segment.text, Modifier.fillMaxWidth(), fontSizeMultiplier, useKatex, searchQuery, searchFocusedOccurrence, onFocusedOccurrencePositioned)</ID>
<ID>MaxLineLength:SharedContentParts.kt$MarkdownContent(thinkingText, fontSizeMultiplier = fontSizeMultiplier, useKatex = useKatex, searchQuery = searchQuery, searchFocusedOccurrence = searchFocusedOccurrence, onFocusedOccurrencePositioned = onFocusedOccurrencePositioned)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Text(stringResource(Res.string.label_input), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Text(stringResource(Res.string.label_output), style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.onSurfaceVariant)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Text(stringResource(Res.string.label_thinking), style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.primary, modifier = Modifier.weight(1f))</ID>
<ID>MaxLineLength:SharedContentParts.kt$Text(stringResource(Res.string.video_not_supported), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurfaceVariant)</ID>
<ID>MaxLineLength:SharedContentParts.kt$Text(toolName, style = MaterialTheme.typography.titleSmall, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.weight(1f))</ID>
<ID>ModifierMissing:CommonChatInputCore.kt$ChatInputPlaceholder</ID>
<ID>ModifierWithoutDefault:SharedContentParts.kt$modifier</ID>
<ID>NoConsecutiveBlankLines:MarkdownParsing.kt$ </ID>
<ID>PackageName:ExpectResourceCollectors.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageName:Res.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageName:String0.commonMain.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageName:String1.commonMain.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageName:String2.commonMain.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageNaming:ExpectResourceCollectors.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageNaming:Res.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageNaming:String0.commonMain.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageNaming:String1.commonMain.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>PackageNaming:String2.commonMain.kt$package librechat_mobile.feature.chat.generated.resources</ID>
<ID>ParameterNaming:ComparisonTabBar.kt$onTabChanged</ID>
<ID>ParameterNaming:InConvoSearchBar.kt$onQueryChanged</ID>
<ID>ParameterNaming:InlineEditInput.kt$onTextChanged</ID>
<ID>ParameterNaming:MessageContentAndActions.kt$onEditTextChanged</ID>
<ID>ParameterNaming:MessageContentAndActions.kt$onFocusedOccurrencePositioned</ID>
<ID>ParameterNaming:MessageList.kt$onEditTextChanged</ID>
<ID>ParameterNaming:MessageList.kt$onSearchScrollHandled</ID>
<ID>ParameterNaming:ModelSelectorSheet.kt$onModelSelected</ID>
<ID>ParameterNaming:PlatformChatComponents.kt$onEditTextChanged</ID>
<ID>ParameterNaming:PlatformChatComponents.kt$onFocusedOccurrencePositioned</ID>
<ID>ParameterNaming:PlatformScreens.kt$onConversationStarted</ID>
<ID>ParameterNaming:PresetPicker.kt$onPresetSelected</ID>
<ID>ParameterNaming:PromptFilterSheet.kt$onCategorySelected</ID>
<ID>ParameterNaming:PromptFilterSheet.kt$onSortOrderChanged</ID>
<ID>ParameterNaming:PromptShareDialog.kt$onShareToggled</ID>
<ID>ParameterNaming:PromptVariablesSection.kt$onVariableChanged</ID>
<ID>ParameterNaming:SharedContentParts.kt$onFocusedOccurrencePositioned</ID>
<ID>ParameterNaming:SlashCommandMenu.kt$onCommandSelected</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:ChatNavigation.kt$Chat : ChatRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:ChatNavigation.kt$PromptEditor : ChatRoute</ID>
<ID>SpacingBetweenDeclarationsWithAnnotations:ChatNavigation.kt$PromptsLibrary : ChatRoute</ID>
<ID>UnusedPrivateProperty:ChatViewModel.kt$ChatViewModel$savedStateHandle: SavedStateHandle</ID>
<ID>UnusedPrivateProperty:PromptEditorViewModel.kt$PromptEditorViewModel$savedStateHandle: SavedStateHandle</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -32,12 +32,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
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_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import kotlinx.coroutines.delay
import co.touchlab.kermit.Logger
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.stringResource
import java.io.File
import java.util.Locale

View file

@ -40,7 +40,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.role
@ -53,8 +52,9 @@ import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.garfiec.librechat.feature.chat.model.McpServerDisplayData
import com.garfiec.librechat.feature.chat.model.PromptMentionDisplayData
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import org.jetbrains.compose.resources.stringResource
import java.io.File
@Composable
@ -371,7 +371,7 @@ fun ChatInput(
// Slash command dropdown
SlashCommandMenu(
filteredCommands = filteredSlashCommands,
onCommandSelected = onSlashCommandSelected,
onCommandSelect = onSlashCommandSelected,
)
}
},

View file

@ -17,7 +17,7 @@ actual fun ContentPartRenderer(
showImageDescriptions: Boolean,
searchQuery: String?,
searchFocusedOccurrence: Int,
onFocusedOccurrencePositioned: ((LayoutCoordinates) -> Unit)?,
onFocusedOccurrencePosition: ((LayoutCoordinates) -> Unit)?,
) {
ContentPartDispatcher(
part = part,
@ -29,6 +29,6 @@ actual fun ContentPartRenderer(
showImageDescriptions = showImageDescriptions,
searchQuery = searchQuery,
searchFocusedOccurrence = searchFocusedOccurrence,
onFocusedOccurrencePositioned = onFocusedOccurrencePositioned,
onFocusedOccurrencePosition = onFocusedOccurrencePosition,
)
}

View file

@ -43,21 +43,21 @@ import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import coil3.compose.AsyncImage
import coil3.SingletonImageLoader
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import coil3.toBitmap
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.compose.resources.stringResource
import java.io.File
@Composable

View file

@ -44,7 +44,6 @@ import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalUriHandler
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextStyle
@ -60,11 +59,12 @@ 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_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import com.mikepenz.markdown.m3.Markdown
import com.mikepenz.markdown.m3.markdownColor
import com.mikepenz.markdown.m3.markdownTypography
import org.jetbrains.compose.resources.stringResource
/**
* Markdown content rendering with full CommonMark support. Uses a hybrid approach:
@ -82,7 +82,7 @@ import com.mikepenz.markdown.m3.markdownTypography
* @param searchFocusedOccurrence When >= 0, the occurrence at this index (counted
* across all text segments in this message, 0-based) is highlighted in orange
* instead of yellow to indicate the currently focused search result.
* @param onFocusedOccurrencePositioned Callback invoked with the [LayoutCoordinates]
* @param onFocusedOccurrencePosition Callback invoked with the [LayoutCoordinates]
* of the text segment containing the focused search occurrence, after it has been
* laid out. Used by the parent to fine-tune scroll position within a long message.
*/
@ -94,7 +94,7 @@ actual fun MarkdownContent(
useKatex: Boolean,
searchQuery: String?,
searchFocusedOccurrence: Int,
onFocusedOccurrencePositioned: ((LayoutCoordinates) -> Unit)?,
onFocusedOccurrencePosition: ((LayoutCoordinates) -> Unit)?,
) {
val segments = remember(text) { parseMarkdownSegments(text) }
val isSearchActive = !searchQuery.isNullOrBlank()
@ -155,7 +155,7 @@ actual fun MarkdownContent(
searchQuery = searchQuery,
focusedOccurrence = focusedInSegment,
fontSizeMultiplier = fontSizeMultiplier,
onPositioned = if (hasFocus) onFocusedOccurrencePositioned else null,
onPositioned = if (hasFocus) onFocusedOccurrencePosition else null,
)
occurrenceOffset += segmentOccurrences
} else {
@ -201,7 +201,7 @@ actual fun MarkdownContent(
searchQuery = searchQuery,
focusedOccurrence = focusedInSegment,
fontSizeMultiplier = fontSizeMultiplier,
onPositioned = if (hasFocus) onFocusedOccurrencePositioned else null,
onPositioned = if (hasFocus) onFocusedOccurrencePosition else null,
)
occurrenceOffset += segmentOccurrences
} else {
@ -326,7 +326,6 @@ private fun MarkdownTextSegment(
}
}
/**
* Wraps a [MarkdownTable] in a [Box] with a fullscreen expand button overlaid on
* the top-right corner. Tapping the button opens a [FullscreenTableDialog].
@ -590,4 +589,3 @@ private fun TableCell(
)
}
}

View file

@ -42,14 +42,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.toArgb
import org.jetbrains.compose.resources.stringResource
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_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import co.touchlab.kermit.Logger
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun MermaidDiagram(

View file

@ -23,7 +23,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.compositeOver
import androidx.compose.ui.layout.LayoutCoordinates
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.garfiec.librechat.core.common.ChatLayoutConstants
@ -31,9 +30,10 @@ 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_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.stringResource
private const val ACTION_AUTO_HIDE_MILLIS = 30_000L
@ -57,7 +57,7 @@ actual fun MessageBubble(
currentFeedback: String?,
isEditing: Boolean,
editText: String,
onEditTextChanged: ((String) -> Unit)?,
onEditTextChange: ((String) -> Unit)?,
onEditSaveAndSubmit: (() -> Unit)?,
onEditSaveOnly: (() -> Unit)?,
onEditCancel: (() -> Unit)?,
@ -70,7 +70,7 @@ actual fun MessageBubble(
isSearchMatch: Boolean,
isCurrentSearchMatch: Boolean,
searchFocusedOccurrence: Int,
onFocusedOccurrencePositioned: ((LayoutCoordinates) -> Unit)?,
onFocusedOccurrencePosition: ((LayoutCoordinates) -> Unit)?,
useKatex: Boolean,
chatLayoutStyle: String,
showAvatars: Boolean,
@ -97,7 +97,7 @@ actual fun MessageBubble(
currentFeedback = currentFeedback,
isEditing = isEditing,
editText = editText,
onEditTextChanged = onEditTextChanged,
onEditTextChange = onEditTextChange,
onEditSaveAndSubmit = onEditSaveAndSubmit,
onEditSaveOnly = onEditSaveOnly,
onEditCancel = onEditCancel,
@ -110,7 +110,7 @@ actual fun MessageBubble(
isSearchMatch = isSearchMatch,
isCurrentSearchMatch = isCurrentSearchMatch,
searchFocusedOccurrence = searchFocusedOccurrence,
onFocusedOccurrencePositioned = onFocusedOccurrencePositioned,
onFocusedOccurrencePosition = onFocusedOccurrencePosition,
showAvatars = showAvatars,
showBubbles = showBubbles,
)
@ -135,7 +135,7 @@ actual fun MessageBubble(
currentFeedback = currentFeedback,
isEditing = isEditing,
editText = editText,
onEditTextChanged = onEditTextChanged,
onEditTextChange = onEditTextChange,
onEditSaveAndSubmit = onEditSaveAndSubmit,
onEditSaveOnly = onEditSaveOnly,
onEditCancel = onEditCancel,
@ -148,7 +148,7 @@ actual fun MessageBubble(
isSearchMatch = isSearchMatch,
isCurrentSearchMatch = isCurrentSearchMatch,
searchFocusedOccurrence = searchFocusedOccurrence,
onFocusedOccurrencePositioned = onFocusedOccurrencePositioned,
onFocusedOccurrencePosition = onFocusedOccurrencePosition,
showAvatars = showAvatars,
showBubbles = showBubbles,
)
@ -179,7 +179,7 @@ private fun ThreadMessageBubble(
currentFeedback: String?,
isEditing: Boolean,
editText: String,
onEditTextChanged: ((String) -> Unit)?,
onEditTextChange: ((String) -> Unit)?,
onEditSaveAndSubmit: (() -> Unit)?,
onEditSaveOnly: (() -> Unit)?,
onEditCancel: (() -> Unit)?,
@ -192,7 +192,7 @@ private fun ThreadMessageBubble(
isSearchMatch: Boolean,
isCurrentSearchMatch: Boolean,
searchFocusedOccurrence: Int,
onFocusedOccurrencePositioned: ((LayoutCoordinates) -> Unit)?,
onFocusedOccurrencePosition: ((LayoutCoordinates) -> Unit)?,
showAvatars: Boolean,
showBubbles: Boolean,
) {
@ -326,7 +326,7 @@ private fun ThreadMessageBubble(
isUser = isUser,
isEditing = isEditing,
editText = editText,
onEditTextChanged = onEditTextChanged,
onEditTextChange = onEditTextChange,
onEditSaveAndSubmit = onEditSaveAndSubmit,
onEditSaveOnly = onEditSaveOnly,
onEditCancel = onEditCancel,
@ -338,7 +338,7 @@ private fun ThreadMessageBubble(
isSearchMatch = isSearchMatch,
isCurrentSearchMatch = isCurrentSearchMatch,
searchFocusedOccurrence = searchFocusedOccurrence,
onFocusedOccurrencePositioned = onFocusedOccurrencePositioned,
onFocusedOccurrencePosition = onFocusedOccurrencePosition,
showActions = showActions,
siblingIndex = siblingIndex,
siblingCount = siblingCount,
@ -382,7 +382,7 @@ private fun TwoSidedMessageBubble(
currentFeedback: String?,
isEditing: Boolean,
editText: String,
onEditTextChanged: ((String) -> Unit)?,
onEditTextChange: ((String) -> Unit)?,
onEditSaveAndSubmit: (() -> Unit)?,
onEditSaveOnly: (() -> Unit)?,
onEditCancel: (() -> Unit)?,
@ -395,7 +395,7 @@ private fun TwoSidedMessageBubble(
isSearchMatch: Boolean,
isCurrentSearchMatch: Boolean,
searchFocusedOccurrence: Int,
onFocusedOccurrencePositioned: ((LayoutCoordinates) -> Unit)?,
onFocusedOccurrencePosition: ((LayoutCoordinates) -> Unit)?,
showAvatars: Boolean,
showBubbles: Boolean,
) {
@ -538,7 +538,7 @@ private fun TwoSidedMessageBubble(
isUser = isUser,
isEditing = isEditing,
editText = editText,
onEditTextChanged = onEditTextChanged,
onEditTextChange = onEditTextChange,
onEditSaveAndSubmit = onEditSaveAndSubmit,
onEditSaveOnly = onEditSaveOnly,
onEditCancel = onEditCancel,
@ -550,7 +550,7 @@ private fun TwoSidedMessageBubble(
isSearchMatch = isSearchMatch,
isCurrentSearchMatch = isCurrentSearchMatch,
searchFocusedOccurrence = searchFocusedOccurrence,
onFocusedOccurrencePositioned = onFocusedOccurrencePositioned,
onFocusedOccurrencePosition = onFocusedOccurrencePosition,
showActions = showActions,
siblingIndex = siblingIndex,
siblingCount = siblingCount,
@ -580,4 +580,3 @@ private fun TwoSidedMessageBubble(
}
}
}

View file

@ -23,14 +23,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalContext
import org.jetbrains.compose.resources.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import librechat_mobile.feature.chat.generated.resources.Res
import librechat_mobile.feature.chat.generated.resources.*
import com.garfiec.librechat.feature.chat.resources.*
import com.garfiec.librechat.feature.chat.resources.Res
import org.jetbrains.compose.resources.stringResource
@Composable
actual fun VideoContent(

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