|
|
@@ -1,43 +1,61 @@
|
|
|
package eu.mjdev.desktop.components.controlcenter.pages
|
|
|
|
|
|
import androidx.compose.desktop.ui.tooling.preview.Preview
|
|
|
+import androidx.compose.foundation.ExperimentalFoundationApi
|
|
|
import androidx.compose.foundation.background
|
|
|
import androidx.compose.foundation.layout.*
|
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
|
import androidx.compose.foundation.lazy.itemsIndexed
|
|
|
+import androidx.compose.foundation.onClick
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
-import androidx.compose.material.OutlinedTextField
|
|
|
-import androidx.compose.material.Text
|
|
|
+import androidx.compose.material.Icon
|
|
|
import androidx.compose.material.TextFieldDefaults
|
|
|
-import androidx.compose.material.icons.Icons
|
|
|
-import androidx.compose.material.icons.filled.Campaign
|
|
|
import androidx.compose.runtime.Composable
|
|
|
+import androidx.compose.runtime.derivedStateOf
|
|
|
import androidx.compose.runtime.mutableStateListOf
|
|
|
+import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
+import androidx.compose.ui.platform.LocalClipboardManager
|
|
|
+import androidx.compose.ui.text.AnnotatedString
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
import eu.mjdev.desktop.components.controlcenter.base.ControlCenterPage
|
|
|
import eu.mjdev.desktop.components.controlcenter.base.ControlCenterPageScope.Companion.remember
|
|
|
+import eu.mjdev.desktop.components.input.SelectableOutlineEditText
|
|
|
+import eu.mjdev.desktop.components.text.TextAny
|
|
|
import eu.mjdev.desktop.extensions.Compose.rememberState
|
|
|
-import kotlinx.coroutines.launch
|
|
|
+import eu.mjdev.desktop.icons.Icons
|
|
|
|
|
|
+// todo copy only selected text to clipboard
|
|
|
+@OptIn(ExperimentalFoundationApi::class)
|
|
|
@Suppress("FunctionName")
|
|
|
fun AIPage() = ControlCenterPage(
|
|
|
- icon = Icons.Filled.Campaign,
|
|
|
+ icon = Icons.Chat,
|
|
|
name = "AI",
|
|
|
condition = { ai.isAvailable() }
|
|
|
) {
|
|
|
+ val clipboardManager = LocalClipboardManager.current
|
|
|
+ val iconSize = 24.dp
|
|
|
val questionsList = remember { mutableStateListOf<Pair<String, String>>() }
|
|
|
val request = rememberState("")
|
|
|
- val onDone: (String) -> Unit = { what ->
|
|
|
+ val iconSend = androidx.compose.runtime.remember {
|
|
|
+ derivedStateOf {
|
|
|
+ if (request.value.isNotEmpty()) Icons.SendIcon else null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ val onDone: () -> Unit = {
|
|
|
+ val what = request.value.replace("\n", "")
|
|
|
if (what.isNotEmpty()) {
|
|
|
questionsList.add(Pair(what, ""))
|
|
|
+ request.value = ""
|
|
|
api.ai.ask(what) { _, res ->
|
|
|
- res.replace("* **", "").replace("**", "").let { resx ->
|
|
|
- questionsList.removeIf { it.first == what }
|
|
|
- questionsList.add(Pair(what, resx))
|
|
|
- talk(resx)
|
|
|
- }
|
|
|
+ res.replace("* **", "")
|
|
|
+ .replace("**", "")
|
|
|
+ .let { resx ->
|
|
|
+ questionsList.removeIf { it.first == what }
|
|
|
+ questionsList.add(Pair(what, resx))
|
|
|
+ talk(resx)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -53,44 +71,84 @@ fun AIPage() = ControlCenterPage(
|
|
|
LazyColumn(
|
|
|
modifier = Modifier.fillMaxWidth()
|
|
|
.weight(1f)
|
|
|
- .background(Color.Black.copy(alpha = 0.3f), RoundedCornerShape(8.dp)),
|
|
|
+ .background(
|
|
|
+ Color.Black.copy(alpha = 0.3f),
|
|
|
+ RoundedCornerShape(8.dp)
|
|
|
+ )
|
|
|
) {
|
|
|
itemsIndexed(questionsList) { idx, textData ->
|
|
|
Column(
|
|
|
modifier = Modifier.padding(4.dp)
|
|
|
.fillMaxWidth()
|
|
|
- .background(Color.White.copy(alpha = 0.1f), RoundedCornerShape(8.dp))
|
|
|
+ .background(
|
|
|
+ Color.White.copy(alpha = 0.1f),
|
|
|
+ RoundedCornerShape(8.dp)
|
|
|
+ )
|
|
|
.padding(4.dp),
|
|
|
) {
|
|
|
- Text(
|
|
|
- modifier = Modifier.padding(4.dp)
|
|
|
+ Box(
|
|
|
+ Modifier.padding(4.dp)
|
|
|
.fillMaxWidth()
|
|
|
- .background(Color.White.copy(alpha = 0.1f), RoundedCornerShape(8.dp))
|
|
|
- .padding(8.dp),
|
|
|
- text = "${idx + 1}. ${textData.first}",
|
|
|
- color = Color.White
|
|
|
- )
|
|
|
- Text(
|
|
|
+ .background(
|
|
|
+ Color.White.copy(alpha = 0.1f),
|
|
|
+ RoundedCornerShape(8.dp)
|
|
|
+ )
|
|
|
+ .padding(8.dp)
|
|
|
+ ) {
|
|
|
+ TextAny(
|
|
|
+ modifier = Modifier.fillMaxWidth().padding(end = 24.dp),
|
|
|
+ text = "${idx + 1}. ${textData.first}",
|
|
|
+ color = Color.White
|
|
|
+ )
|
|
|
+ Icon(
|
|
|
+ modifier = Modifier.padding(4.dp)
|
|
|
+ .size(iconSize)
|
|
|
+ .align(Alignment.TopEnd)
|
|
|
+ .onClick {
|
|
|
+ clipboardManager.setText(AnnotatedString(textData.first))
|
|
|
+ },
|
|
|
+ imageVector = Icons.CopyToClipboard,
|
|
|
+ tint = Color.White.copy(alpha = 0.9f),
|
|
|
+ contentDescription = ""
|
|
|
+ )
|
|
|
+ }
|
|
|
+ Box(
|
|
|
modifier = Modifier.padding(4.dp)
|
|
|
.fillMaxWidth()
|
|
|
- .background(Color.White.copy(alpha = 0.1f), RoundedCornerShape(8.dp))
|
|
|
+ .background(
|
|
|
+ Color.White.copy(alpha = 0.1f),
|
|
|
+ RoundedCornerShape(8.dp)
|
|
|
+ )
|
|
|
.padding(8.dp),
|
|
|
- text = textData.second,
|
|
|
- color = Color.White.copy(alpha = 0.9f)
|
|
|
- )
|
|
|
+ ) {
|
|
|
+ TextAny(
|
|
|
+ modifier = Modifier.fillMaxWidth().padding(end = 24.dp),
|
|
|
+ text = textData.second,
|
|
|
+ color = Color.White.copy(alpha = 0.9f)
|
|
|
+ )
|
|
|
+ Icon(
|
|
|
+ modifier = Modifier.padding(4.dp)
|
|
|
+ .size(iconSize)
|
|
|
+ .align(Alignment.TopEnd)
|
|
|
+ .onClick {
|
|
|
+ clipboardManager.setText(AnnotatedString(textData.second))
|
|
|
+ },
|
|
|
+ imageVector = Icons.CopyToClipboard,
|
|
|
+ tint = Color.White.copy(alpha = 0.9f),
|
|
|
+ contentDescription = ""
|
|
|
+ )
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- OutlinedTextField(
|
|
|
- modifier = Modifier.padding(top = 8.dp).fillMaxWidth(),
|
|
|
+ SelectableOutlineEditText(
|
|
|
+ modifier = Modifier.padding(top = 8.dp)
|
|
|
+ .fillMaxWidth(),
|
|
|
value = request.value,
|
|
|
onValueChange = { t: String ->
|
|
|
request.value = t
|
|
|
if (t.contains("\n")) {
|
|
|
- scope.launch {
|
|
|
- onDone(t.replace("\n", ""))
|
|
|
- }
|
|
|
- request.value = ""
|
|
|
+ onDone()
|
|
|
}
|
|
|
},
|
|
|
maxLines = 4,
|
|
|
@@ -102,7 +160,35 @@ fun AIPage() = ControlCenterPage(
|
|
|
cursorColor = Color.White,
|
|
|
focusedBorderColor = Color.White,
|
|
|
unfocusedBorderColor = Color.Black
|
|
|
- )
|
|
|
+ ),
|
|
|
+ trailingIcon = {
|
|
|
+ Row(
|
|
|
+ verticalAlignment = Alignment.CenterVertically,
|
|
|
+ ) {
|
|
|
+ if (request.value.isNotEmpty()) {
|
|
|
+ Icon(
|
|
|
+ modifier = Modifier.padding(4.dp)
|
|
|
+ .size(iconSize)
|
|
|
+ .onClick {
|
|
|
+ clipboardManager.setText(AnnotatedString(request.value))
|
|
|
+ },
|
|
|
+ imageVector = Icons.CopyToClipboard,
|
|
|
+ tint = Color.White.copy(alpha = 0.9f),
|
|
|
+ contentDescription = ""
|
|
|
+ )
|
|
|
+ }
|
|
|
+ if (iconSend.value != null) {
|
|
|
+ Icon(
|
|
|
+ modifier = Modifier.padding(4.dp)
|
|
|
+ .size(iconSize)
|
|
|
+ .onClick { onDone() },
|
|
|
+ imageVector = iconSend.value!!,
|
|
|
+ tint = Color.White.copy(alpha = 0.9f),
|
|
|
+ contentDescription = ""
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
@@ -111,4 +197,4 @@ fun AIPage() = ControlCenterPage(
|
|
|
|
|
|
@Preview
|
|
|
@Composable
|
|
|
-fun AIPagePreview() = AIPage().render()
|
|
|
+fun AIPagePreview() = AIPage().render()
|