diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml index c6e9d43..be03643 100644 --- a/composeApp/src/androidMain/AndroidManifest.xml +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -7,6 +7,7 @@ android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:name=".ComfyApp" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:usesCleartextTraffic="true" diff --git a/composeApp/src/androidMain/kotlin/moe/uni/comfy/comfyuikmp/ComfyApp.kt b/composeApp/src/androidMain/kotlin/moe/uni/comfy/comfyuikmp/ComfyApp.kt new file mode 100644 index 0000000..2657e51 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/moe/uni/comfy/comfyuikmp/ComfyApp.kt @@ -0,0 +1,17 @@ +package moe.uni.comfy.comfyuikmp + +import android.annotation.SuppressLint +import android.app.Application +import android.content.Context + +class ComfyApp: Application() { + companion object{ + @SuppressLint("StaticFieldLeak") + lateinit var context: Context + } + + override fun onCreate() { + super.onCreate() + context = applicationContext + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUrl.android.kt b/composeApp/src/androidMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUrl.android.kt new file mode 100644 index 0000000..f9a9448 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUrl.android.kt @@ -0,0 +1,58 @@ +package moe.uni.comfy.comfyuikmp.util + +import android.content.ContentValues +import android.graphics.Bitmap +import android.os.Build +import android.os.Environment +import android.provider.MediaStore +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asAndroidBitmap +import moe.uni.comfy.comfyuikmp.ComfyApp +import java.io.File + + +actual suspend fun saveImageToLocal(image: ImageBitmap, name: String): Result { + val bitmap = image.asAndroidBitmap() + + val imagesCollection = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY) + } else { + MediaStore.Images.Media.EXTERNAL_CONTENT_URI + } + + val contentValues = ContentValues().apply { + put(MediaStore.Images.Media.DISPLAY_NAME, "$name.png") + put(MediaStore.Images.Media.MIME_TYPE, "image/png") + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/ComfyApp") + put(MediaStore.Images.Media.IS_PENDING, 1) + } + } + + val resolver = ComfyApp.context.contentResolver + val uri = resolver.insert(imagesCollection, contentValues) + uri?.let { + try { + resolver.openOutputStream(uri)?.use { out -> + bitmap.compress(Bitmap.CompressFormat.PNG, 100, out) + } ?: throw kotlinx.io.IOException("OpenOutputStream returned null") + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.clear() + contentValues.put(MediaStore.Images.Media.IS_PENDING, 0) + resolver.update(uri, contentValues, null, null) + } + return Result.success(true) + } catch (e: Exception) { + // 出错时删除占位条目,避免相册里出现空文件 + runCatching { resolver.delete(uri, null, null) } + e.printStackTrace() + return Result.failure(e) + } + + } + return Result.failure(kotlinx.io.IOException("Failed to create new MediaStore record.")) + + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/model/ComfyModels.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/model/ComfyModels.kt index 09a3440..ea468f6 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/model/ComfyModels.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/model/ComfyModels.kt @@ -4,6 +4,11 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement + +object NodeType{ + const val KSampler = "KSampler" +} + /** * 通用 ComfyUI 节点模型,尽量保持结构宽松以兼容不同节点类型与输入形态。 */ diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/repository/WorkflowRepository.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/repository/WorkflowRepository.kt index 961f59f..55b03e4 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/repository/WorkflowRepository.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/data/repository/WorkflowRepository.kt @@ -6,12 +6,14 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import moe.uni.comfy.comfyuikmp.data.model.ComfyPrompt import moe.uni.comfy.comfyuikmp.data.settings.KeyValueStore +import moe.uni.comfy.comfyuikmp.data.model.ImageRef @Serializable data class WorkflowItem( val id: String, val name: String, - val prompt: ComfyPrompt + val prompt: ComfyPrompt, + val cover: ImageRef? = null ) class WorkflowRepository { diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppRoot.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppRoot.kt index 7c19759..232a906 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppRoot.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppRoot.kt @@ -1,16 +1,21 @@ package moe.uni.comfy.comfyuikmp.ui import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.History +import androidx.compose.material.icons.filled.Save import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import kotlinx.coroutines.launch import kotlinx.serialization.json.JsonPrimitive +import moe.uni.comfy.comfyuikmp.data.model.NodeType import moe.uni.comfy.comfyuikmp.di.AppGraph import moe.uni.comfy.comfyuikmp.ui.gallery.GalleryScreen import moe.uni.comfy.comfyuikmp.ui.generate.GenerateScreen @@ -21,6 +26,7 @@ import moe.uni.comfy.comfyuikmp.ui.navigation.rememberNavController import moe.uni.comfy.comfyuikmp.ui.settings.SettingsScreen import moe.uni.comfy.comfyuikmp.ui.view.ViewScreen import moe.uni.comfy.comfyuikmp.util.mainViewModelFactory +import moe.uni.comfy.comfyuikmp.util.saveImageToLocal import kotlin.random.Random import kotlin.time.Clock import kotlin.time.ExperimentalTime @@ -31,6 +37,7 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory)) val hasSaved = remember { AppGraph.workflows.list().isNotEmpty() } val startDest = if (hasSaved) Dest.Home else Dest.Settings val navController = rememberNavController(startDest) + MaterialTheme { val snackbar = remember { SnackbarHostState() } val scope = rememberCoroutineScope() @@ -72,11 +79,29 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory)) }, actions = { if (current == Dest.Generate) { + if (uiState.image!=null){ + Icon(Icons.Filled.Save, contentDescription = null, modifier = Modifier.clickable{ + uiState.image?.let { + scope.launch { + val result = saveImageToLocal(it,"${uiState.workflowName}_${Random.nextInt()}.png") + + if (result.isSuccess){ + snackbar.showSnackbar("保存成功") + }else{ + snackbar.showSnackbar(result.exceptionOrNull()?.message?:"保存失败") + } + } + } + }) + Spacer(Modifier.size(16.dp)) + } Icon( Icons.Filled.History, contentDescription = null, modifier = Modifier.clickable { navController.navigate(Dest.Gallery) } ) + Spacer(Modifier.size(16.dp)) + } } ) @@ -90,7 +115,7 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory)) uiState.prompt.let { p -> scope.launch { if (uiState.useRandom) { - p.values.find { it.classType == "KSampler" }?.let { + p.values.find { it.classType == NodeType.KSampler }?.let { val inputs = it.inputs.toMutableMap() inputs["seed"] = JsonPrimitive(Random.nextInt(0, Int.MAX_VALUE)) it.inputs = inputs @@ -145,7 +170,8 @@ fun AppRoot(viewModel: AppViewModel = viewModel(factory = mainViewModelFactory)) viewModel.useRandomSeed(it) }, - onOpenImage = { ref -> viewModel.openImage(ref) } + onOpenImage = { ref -> viewModel.openImage(ref) }, + onSetCover = { } ) } diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppViewModel.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppViewModel.kt index 1019ffa..971e8a8 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppViewModel.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/AppViewModel.kt @@ -23,6 +23,7 @@ import moe.uni.comfy.comfyuikmp.data.network.createDefaultHttpClient import moe.uni.comfy.comfyuikmp.data.repository.WorkflowItem import moe.uni.comfy.comfyuikmp.di.AppGraph import moe.uni.comfy.comfyuikmp.util.decodeImage +import moe.uni.comfy.comfyuikmp.util.saveImageToLocal class AppViewModel : ViewModel() { private val _uiState = MutableStateFlow(AppUiState()) @@ -145,5 +146,5 @@ data class AppUiState( val image: ImageBitmap? = null, val progress:ProgressUpdate = ProgressUpdate(0f, ""), val prompt: ComfyPrompt = emptyMap(), - val useRandom: Boolean = false + val useRandom: Boolean = true ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/gallery/GalleryScreen.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/gallery/GalleryScreen.kt index 6c9f4f1..0204a7f 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/gallery/GalleryScreen.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/gallery/GalleryScreen.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import androidx.compose.material3.Card import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -23,6 +24,12 @@ import coil3.compose.AsyncImage import moe.uni.comfy.comfyuikmp.di.AppGraph import moe.uni.comfy.comfyuikmp.ui.AppViewModel import moe.uni.comfy.comfyuikmp.ui.navigation.Dest +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import moe.uni.comfy.comfyuikmp.util.buildImageUrl @OptIn(ExperimentalFoundationApi::class) @Composable @@ -42,23 +49,12 @@ fun GalleryScreen(contentPadding: PaddingValues, viewModel: AppViewModel) { LazyColumn(contentPadding = contentPadding, modifier = Modifier.padding(horizontal = 16.dp)) { items(history, key = { it.filename }) { img -> - val url = buildString { - append(base) - append("/view?filename=") - append(img.filename) - img.subfolder?.let { - append("&subfolder=") - append(it) - } - img.type?.let { - append("&type=") - append(it) - } - } + val url = buildImageUrl(img) - Card(modifier = Modifier.fillMaxWidth().aspectRatio(3f / 4f).clickable { - viewModel.openImage(img) - }) { + + Card(modifier = Modifier.fillMaxWidth().aspectRatio(3f / 4f).combinedClickable( + onClick = { viewModel.openImage(img) } + )) { AsyncImage( model = url, contentDescription = null, diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/generate/GenerateScreen.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/generate/GenerateScreen.kt index 89a5007..c077d23 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/generate/GenerateScreen.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/generate/GenerateScreen.kt @@ -26,6 +26,7 @@ import androidx.compose.material3.Switch import androidx.compose.material3.rememberBottomSheetScaffoldState import androidx.compose.ui.Alignment import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.booleanOrNull @@ -36,6 +37,9 @@ import moe.uni.comfy.comfyuikmp.data.model.updateNodeStringInput import moe.uni.comfy.comfyuikmp.data.model.updateNodePrimitiveInput import moe.uni.comfy.comfyuikmp.data.network.ProgressUpdate import moe.uni.comfy.comfyuikmp.data.model.ImageRef +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import moe.uni.comfy.comfyuikmp.data.model.NodeType @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -49,6 +53,7 @@ fun GenerateScreen( onPromptChange: (ComfyPrompt) -> Unit, onRandomSwitchChange: (Boolean) -> Unit, onOpenImage: (ImageRef) -> Unit = {}, + onSetCover: (ImageRef) -> Unit = {}, ) { val state = rememberBottomSheetScaffoldState() Scaffold(modifier = Modifier.padding(contentPadding)) { @@ -141,7 +146,8 @@ fun GenerateScreen( ) //KSampler - if (node.classType == "KSampler" && key.equals("seed", ignoreCase = true)) { + if (node.classType == NodeType.KSampler && key.equals("seed", ignoreCase = true)) { + inputEnable = !useRandom Row(modifier = Modifier.padding(top = 8.dp)) { Spacer(Modifier.width(8.dp)) Switch(useRandom, onCheckedChange = { @@ -189,6 +195,7 @@ fun GenerateScreen( Alignment.Center)) } + var showSetCover by remember { mutableStateOf(false) } if (latestImage != null) { if (latestImageRef != null) { Image( @@ -197,7 +204,10 @@ fun GenerateScreen( modifier = Modifier .fillMaxWidth() .padding(top = 32.dp) - .clickable { onOpenImage(latestImageRef) } + .combinedClickable( + onClick = { onOpenImage(latestImageRef) }, + onLongClick = { showSetCover = true } + ) ) } else { Image( @@ -207,6 +217,21 @@ fun GenerateScreen( ) } } + + if (showSetCover && latestImageRef != null) { + AlertDialog( + onDismissRequest = { showSetCover = false }, + confirmButton = { + Button(onClick = { + onSetCover(latestImageRef) + showSetCover = false + }) { Text("设为封面") } + }, + dismissButton = { Button(onClick = { showSetCover = false }) { Text("取消") } }, + title = { Text("设置封面") }, + text = { Text("将当前图片设为该工作流的封面?") } + ) + } } } } diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/home/HomeScreen.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/home/HomeScreen.kt index 0d28b6b..c77d891 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/home/HomeScreen.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/home/HomeScreen.kt @@ -6,12 +6,16 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.Column 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.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -28,6 +32,10 @@ import androidx.compose.material3.Icon import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.Alignment +import coil3.compose.AsyncImage +import moe.uni.comfy.comfyuikmp.util.buildImageUrl @Composable @@ -49,27 +57,48 @@ fun HomeScreen( ) { Text("新建/导入工作流", modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium) } } items(items) { w -> - Card(modifier = Modifier - .padding(horizontal = 16.dp, vertical = 8.dp) - .fillMaxWidth() - .clickable { onPick(w) } + Card( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .fillMaxWidth() + .clickable { onPick(w) }, + elevation = CardDefaults.cardElevation(defaultElevation = 1.dp) ) { - Text(w.name, modifier = Modifier.padding(16.dp), style = MaterialTheme.typography.titleMedium) - Row(modifier = Modifier.padding(horizontal = 8.dp)) { - IconButton(onClick = { renaming = w; newName = w.name }) { Icon(Icons.Filled.Edit, contentDescription = null, - Modifier.size(24.dp)) } - IconButton(onClick = { AppGraph.workflows.delete(w.id); items = repo.list() }) { Icon(Icons.Filled.Delete, contentDescription = null, - Modifier.size(24.dp)) } + if (w.cover != null) { + AsyncImage( + model = buildImageUrl(w.cover), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxWidth() + .aspectRatio(4f / 3f) + ) + } else { + // 占位封面区域 + androidx.compose.foundation.layout.Box( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(4f / 3f) + ) + } + + Column(modifier = Modifier.padding(12.dp)) { + Text(w.name, style = MaterialTheme.typography.titleMedium) + Row(modifier = Modifier.padding(top = 4.dp), verticalAlignment = Alignment.CenterVertically) { + IconButton(onClick = { renaming = w; newName = w.name }) { Icon(Icons.Filled.Edit, contentDescription = null, + Modifier.size(24.dp)) } + IconButton(onClick = { AppGraph.workflows.delete(w.id); items = repo.list() }) { Icon(Icons.Filled.Delete, contentDescription = null, + Modifier.size(24.dp)) } + } } } } } - val r = renaming - if (r != null) { + if (renaming != null) { AlertDialog(onDismissRequest = { renaming = null }, confirmButton = { Button(onClick = { - AppGraph.workflows.rename(r.id, newName.ifBlank { r.name }) + AppGraph.workflows.rename(renaming!!.id, newName.ifBlank { renaming!!.name }) items = repo.list() renaming = null }) { Text("确定") } diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/view/ViewScreen.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/view/ViewScreen.kt index fe0c6e2..09d5345 100644 --- a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/view/ViewScreen.kt +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/ui/view/ViewScreen.kt @@ -19,24 +19,13 @@ import moe.uni.comfy.comfyuikmp.data.model.ImageRef import moe.uni.comfy.comfyuikmp.di.AppGraph import androidx.compose.foundation.layout.padding import androidx.compose.ui.geometry.Offset +import moe.uni.comfy.comfyuikmp.util.buildImageUrl @Composable fun ViewScreen(ref: ImageRef, contentPadding: PaddingValues) { val base = AppGraph.settings.getBaseUrl() ?: "" val url = remember(ref, base) { - buildString { - append(base) - append("/view?filename=") - append(ref.filename) - ref.subfolder?.let { - append("&subfolder=") - append(it) - } - ref.type?.let { - append("&type=") - append(it) - } - } + buildImageUrl(ref) } val scaleState = remember { mutableFloatStateOf(1f) } diff --git a/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUtils.kt b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUtils.kt new file mode 100644 index 0000000..2aedc93 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUtils.kt @@ -0,0 +1,27 @@ +package moe.uni.comfy.comfyuikmp.util + +import androidx.compose.ui.graphics.ImageBitmap +import moe.uni.comfy.comfyuikmp.data.model.ImageRef +import moe.uni.comfy.comfyuikmp.di.AppGraph + +fun buildImageUrl(ref: ImageRef): String { + val base = AppGraph.settings.getBaseUrl() ?: "" + return buildString { + append(base) + append("/view?filename=") + append(ref.filename) + ref.subfolder?.let { + append("&subfolder=") + append(it) + } + ref.type?.let { + append("&type=") + append(it) + } + } +} + +expect suspend fun saveImageToLocal(image: ImageBitmap, name: String): Result + + + diff --git a/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/PlatformHttpClient.ios.kt b/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/PlatformHttpClient.ios.kt index 31f2a0d..e9768a0 100644 --- a/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/PlatformHttpClient.ios.kt +++ b/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/PlatformHttpClient.ios.kt @@ -19,11 +19,6 @@ actual fun createPlatformHttpClient(configure: HttpClientConfig<*>.() -> Unit): install(UserAgent) { agent = "ComfyUIKMP-iOS" } - defaultRequest { - url { - protocol = URLProtocol.HTTP - } - } configure() } } diff --git a/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUrl.ios.kt b/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUrl.ios.kt new file mode 100644 index 0000000..2cafea3 --- /dev/null +++ b/composeApp/src/iosMain/kotlin/moe/uni/comfy/comfyuikmp/util/ImageUrl.ios.kt @@ -0,0 +1,8 @@ +package moe.uni.comfy.comfyuikmp.util + +import androidx.compose.ui.graphics.ImageBitmap +import coil3.Bitmap + +actual suspend fun saveImageToLocal(image: ImageBitmap, name: String): Result { + return Result.failure(kotlinx.io.IOException("Failed to create new MediaStore record.")) +} diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist index c7e6844..ffbb26f 100644 --- a/iosApp/iosApp/Info.plist +++ b/iosApp/iosApp/Info.plist @@ -4,12 +4,47 @@ CADisableMinimumFrameDurationOnPhone + NSLocalNetworkUsageDescription + 需要访问 ComfyUI 服务以生成图像 NSAppTransportSecurity NSAllowsArbitraryLoads + NSAllowsArbitraryLoadsInWebContent + NSAllowsLocalNetworking + NSExceptionDomains + + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + 127.0.0.1 + + NSExceptionAllowsInsecureHTTPLoads + + + 10.0.0.0/8 + + NSExceptionAllowsInsecureHTTPLoads + + + 172.16.0.0/12 + + NSExceptionAllowsInsecureHTTPLoads + + + 192.168.0.0/16 + + NSExceptionAllowsInsecureHTTPLoads + + +