@@ -5,10 +5,12 @@ package com.shifthackz.aisdv1.presentation.screen.setup
55import androidx.compose.foundation.background
66import androidx.compose.foundation.border
77import androidx.compose.foundation.clickable
8+ import androidx.compose.foundation.layout.Arrangement
89import androidx.compose.foundation.layout.Box
910import androidx.compose.foundation.layout.Column
1011import androidx.compose.foundation.layout.Row
1112import androidx.compose.foundation.layout.Spacer
13+ import androidx.compose.foundation.layout.defaultMinSize
1214import androidx.compose.foundation.layout.fillMaxSize
1315import androidx.compose.foundation.layout.fillMaxWidth
1416import androidx.compose.foundation.layout.height
@@ -28,12 +30,17 @@ import androidx.compose.material.icons.filled.Help
2830import androidx.compose.material.icons.filled.Visibility
2931import androidx.compose.material.icons.filled.VisibilityOff
3032import androidx.compose.material.icons.outlined.ArrowBack
33+ import androidx.compose.material.icons.outlined.FileDownload
34+ import androidx.compose.material.icons.outlined.FileDownloadDone
35+ import androidx.compose.material.icons.outlined.FileDownloadOff
36+ import androidx.compose.material.icons.outlined.Landslide
3137import androidx.compose.material3.Button
3238import androidx.compose.material3.CenterAlignedTopAppBar
3339import androidx.compose.material3.Checkbox
3440import androidx.compose.material3.ExperimentalMaterial3Api
3541import androidx.compose.material3.Icon
3642import androidx.compose.material3.IconButton
43+ import androidx.compose.material3.LinearProgressIndicator
3744import androidx.compose.material3.LocalContentColor
3845import androidx.compose.material3.MaterialTheme
3946import androidx.compose.material3.OutlinedButton
@@ -44,6 +51,7 @@ import androidx.compose.material3.TextField
4451import androidx.compose.runtime.Composable
4552import androidx.compose.ui.Alignment
4653import androidx.compose.ui.Modifier
54+ import androidx.compose.ui.draw.clip
4755import androidx.compose.ui.graphics.Color
4856import androidx.compose.ui.res.stringResource
4957import androidx.compose.ui.text.font.FontWeight
@@ -54,20 +62,21 @@ import androidx.compose.ui.text.style.TextAlign
5462import androidx.compose.ui.tooling.preview.Preview
5563import androidx.compose.ui.unit.dp
5664import androidx.compose.ui.unit.sp
65+ import androidx.compose.ui.unit.times
5766import androidx.lifecycle.compose.collectAsStateWithLifecycle
5867import com.shifthackz.aisdv1.core.common.appbuild.BuildInfoProvider
5968import com.shifthackz.aisdv1.core.common.appbuild.BuildType
6069import com.shifthackz.aisdv1.core.common.links.LinksProvider
6170import com.shifthackz.aisdv1.core.model.asString
6271import com.shifthackz.aisdv1.core.model.asUiText
6372import com.shifthackz.aisdv1.core.ui.MviScreen
73+ import com.shifthackz.aisdv1.domain.entity.DownloadState
6474import com.shifthackz.aisdv1.domain.entity.LocalAiModel
6575import com.shifthackz.aisdv1.presentation.R
6676import com.shifthackz.aisdv1.presentation.utils.Constants
6777import com.shifthackz.aisdv1.presentation.widget.dialog.ErrorDialog
6878import com.shifthackz.aisdv1.presentation.widget.dialog.ProgressDialog
6979import com.shifthackz.aisdv1.presentation.widget.input.DropdownTextField
70- import com.shifthackz.aisdv1.presentation.widget.item.LocalModelItemComposable
7180import com.shifthackz.aisdv1.presentation.widget.item.SettingsItem
7281import org.koin.core.component.KoinComponent
7382import org.koin.core.component.inject
@@ -526,7 +535,7 @@ private fun LocalDiffusionSetupTab(
526535 if (state.localCustomModel) customPredicate else ! customPredicate
527536 }
528537 .forEach { localModel ->
529- LocalModelItemComposable (
538+ LdModelComposable (
530539 model = localModel,
531540 onDownloadCardButtonClick = onDownloadCardButtonClick,
532541 onSelect = onSelectLocalModel,
@@ -588,6 +597,192 @@ private fun ConfigurationModeButton(
588597 }
589598}
590599
600+ @Composable
601+ fun LdModelComposable (
602+ modifier : Modifier = Modifier ,
603+ model : ServerSetupState .LocalModel ,
604+ onDownloadCardButtonClick : (ServerSetupState .LocalModel ) -> Unit = {},
605+ onSelect : (ServerSetupState .LocalModel ) -> Unit = {}
606+ ) {
607+ Column (
608+ modifier = modifier
609+ .padding(vertical = 8 .dp)
610+ .fillMaxWidth()
611+ .clip(RoundedCornerShape (16 .dp))
612+ .background(color = MaterialTheme .colorScheme.surfaceTint.copy(alpha = 0.8f ))
613+ .defaultMinSize(minHeight = 50 .dp)
614+ .border(
615+ width = 2 .dp,
616+ shape = RoundedCornerShape (16 .dp),
617+ color = if (model.selected) MaterialTheme .colorScheme.primary else Color .Transparent ,
618+ )
619+ .clickable { onSelect(model) },
620+ ) {
621+ Row (
622+ modifier = Modifier .padding(vertical = 4 .dp),
623+ horizontalArrangement = Arrangement .Center ,
624+ verticalAlignment = Alignment .CenterVertically ,
625+ ) {
626+ val icon = when (model.downloadState) {
627+ is DownloadState .Downloading -> Icons .Outlined .FileDownload
628+ else -> when {
629+ model.id == LocalAiModel .CUSTOM .id -> Icons .Outlined .Landslide
630+ model.downloaded -> Icons .Outlined .FileDownloadDone
631+ else -> Icons .Outlined .FileDownloadOff
632+ }
633+ }
634+ Icon (
635+ modifier = modifier
636+ .padding(horizontal = 8 .dp)
637+ .size(48 .dp),
638+ imageVector = icon,
639+ contentDescription = " Download state" ,
640+ )
641+ Column (
642+ modifier = Modifier .padding(start = 4 .dp)
643+ ) {
644+ Text (text = model.name)
645+ if (model.id != LocalAiModel .CUSTOM .id) {
646+ Text (model.size)
647+ }
648+ }
649+ Spacer (modifier = Modifier .weight(1f ))
650+ if (model.id != LocalAiModel .CUSTOM .id) {
651+ Button (
652+ modifier = Modifier .padding(end = 8 .dp),
653+ onClick = { onDownloadCardButtonClick(model) },
654+ ) {
655+ Text (
656+ text = stringResource(
657+ id = when (model.downloadState) {
658+ is DownloadState .Downloading -> R .string.cancel
659+ is DownloadState .Error -> R .string.retry
660+ else -> {
661+ if (model.downloaded) R .string.delete
662+ else R .string.download
663+ }
664+ }
665+ ),
666+ color = LocalContentColor .current,
667+ )
668+ }
669+ }
670+ }
671+ if (model.id == LocalAiModel .CUSTOM .id) {
672+ Column (
673+ modifier = Modifier .padding(8 .dp),
674+ ) {
675+ Text (
676+ text = stringResource(id = R .string.model_local_custom_title),
677+ style = MaterialTheme .typography.bodyMedium,
678+ )
679+ Spacer (modifier = Modifier .height(4 .dp))
680+ Text (
681+ text = stringResource(id = R .string.model_local_custom_sub_title),
682+ style = MaterialTheme .typography.bodyMedium,
683+ )
684+ Spacer (modifier = Modifier .height(4 .dp))
685+
686+ fun folderModifier (treeNum : Int ) = Modifier .padding(start = (treeNum - 1 ) * 12 .dp)
687+ val folderStyle = MaterialTheme .typography.bodySmall
688+ Text (
689+ modifier = folderModifier(1 ),
690+ text = " Download" ,
691+ style = folderStyle,
692+ )
693+ Text (
694+ modifier = folderModifier(2 ),
695+ text = " SDAI" ,
696+ style = folderStyle,
697+ )
698+ Text (
699+ modifier = folderModifier(3 ),
700+ text = " model" ,
701+ style = folderStyle,
702+ )
703+
704+ Text (
705+ modifier = folderModifier(4 ),
706+ text = " text_encoder" ,
707+ style = folderStyle,
708+ )
709+ Text (
710+ modifier = folderModifier(5 ),
711+ text = " model.ort" ,
712+ style = folderStyle,
713+ )
714+
715+ Text (
716+ modifier = folderModifier(4 ),
717+ text = " tokenizer" ,
718+ style = folderStyle,
719+ )
720+ Text (
721+ modifier = folderModifier(5 ),
722+ text = " merges.txt" ,
723+ style = folderStyle,
724+ )
725+ Text (
726+ modifier = folderModifier(5 ),
727+ text = " special_tokens_map.json" ,
728+ style = folderStyle,
729+ )
730+ Text (
731+ modifier = folderModifier(5 ),
732+ text = " tokenizer_config.json" ,
733+ style = folderStyle,
734+ )
735+ Text (
736+ modifier = folderModifier(5 ),
737+ text = " tokenizer_config.json" ,
738+ style = folderStyle,
739+ )
740+
741+ Text (
742+ modifier = folderModifier(4 ),
743+ text = " unet" ,
744+ style = folderStyle,
745+ )
746+ Text (
747+ modifier = folderModifier(5 ),
748+ text = " model.ort" ,
749+ style = folderStyle,
750+ )
751+
752+ Text (
753+ modifier = folderModifier(4 ),
754+ text = " vae_decoder" ,
755+ style = folderStyle,
756+ )
757+ Text (
758+ modifier = folderModifier(5 ),
759+ text = " model.ort" ,
760+ style = folderStyle,
761+ )
762+ }
763+ }
764+ when (model.downloadState) {
765+ is DownloadState .Downloading -> {
766+ LinearProgressIndicator (
767+ modifier = Modifier
768+ .padding(8 .dp)
769+ .fillMaxWidth(),
770+ progress = model.downloadState.percent / 100f ,
771+ )
772+ }
773+ is DownloadState .Error -> {
774+ Text (
775+ modifier = Modifier
776+ .padding(horizontal = 8 .dp)
777+ .padding(bottom = 8 .dp),
778+ text = stringResource(id = R .string.error_download_fail),
779+ )
780+ }
781+ else -> Unit
782+ }
783+ }
784+ }
785+
591786@Composable
592787@Preview(showSystemUi = true , showBackground = true )
593788private fun PreviewServerSetupSDAI () {
0 commit comments