profile editing. styling

This commit is contained in:
Joshua Perry 2024-11-02 17:08:33 +00:00
parent d5ff6a1a0a
commit f73be7184f
4 changed files with 218 additions and 32 deletions

View File

@ -1,5 +1,6 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.internal.de.undercouch.gradle.tasks.download.Download
import org.gradle.internal.os.OperatingSystem
plugins {
val kotlinVersion = "2.0.21"
@ -67,35 +68,53 @@ dependencies {
val serializationVersion = "1.5.1"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
//FileKit
val fileKitVersion = "0.8.7"
implementation("io.github.vinceglb:filekit-core:$fileKitVersion")
implementation("io.github.vinceglb:filekit-compose:$fileKitVersion")
}
val currentOs = OperatingSystem.current()
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
linux {
modules("jdk.security.auth")
}
targetFormats(
*listOf(
TargetFormat.Dmg.takeIf { currentOs.isMacOsX },
TargetFormat.Msi.takeIf { currentOs.isWindows },
TargetFormat.Deb.takeIf { currentOs.isLinux }
).filterNotNull().toTypedArray()
)
packageName = "mia"
packageVersion = "1.0.0"
}
}
}
tasks.register("buildLite") {
tasks.register("packageLiteInstaller") {
group = "package project"
description = "Build program with the small vosk STT model"
dependsOn("prepareLiteResources", "build")
dependsOn("prepareLiteResources", "package")
}
tasks.register("buildFull") {
tasks.register("packageFullInstaller") {
group = "package project"
description = "Build program with the large vosk STT model"
dependsOn("prepareFullResources", "build")
dependsOn("prepareFullResources", "package")
}
tasks.register("generateSemanticModel") {
group = "package project"
description = "Run DJL to generate Semantic model from HuggingFace (all-roberta-large-v1)"
val exeDir = ""
val modelName = "sentence-transformers/all-roberta-large-v1"
val djlOutputDir = project.file("src/main/resources/Semantic-model")
@ -106,9 +125,9 @@ tasks.register("generateSemanticModel") {
doLast {
val os = System.getProperty("os.name")
val executable = when {
os.contains("win") -> project.file("$exeDir/djl-windows.exe")
os.contains("mac") -> project.file("$exeDir/djl-mac")
os.contains("linux") || os.contains("nix") || os.contains("nux") -> project.file("$exeDir/djl-linux")
currentOs.isWindows -> project.file("djl/windows.exe")
currentOs.isMacOsX -> project.file("djl/mac")
currentOs.isLinux -> project.file("djl/linux")
else -> throw GradleException("Unsupported OS: $os")
}
@ -123,6 +142,7 @@ val modelUrl = "https://alphacephei.com/vosk/models"
val zipFile = layout.buildDirectory.file("STT-model.zip")
tasks.register<Download>("prepareLiteResources") {
group = "package project"
description = "Download small vosk model to resources"
val modelName = "vosk-model-small-en-us-0.15"
@ -144,6 +164,7 @@ tasks.register<Download>("prepareLiteResources") {
}
tasks.register<Download>("prepareFullResources") {
group = "package project"
description = "Download large vosk model to resources"
val modelName = "vosk-model-en-us-0.22"

View File

@ -79,7 +79,3 @@ fun transparentButton(icon: ImageVector, contentDescription: String, onClick: ()
)
}
}
//TODO: If folders (Semantic-model/, STT-model/) don't exist on start-up install
// Install Semantic-model by downloading, using djl-converter (look into using pip module from Java), move to resources
// Install STT-model by downloading, move to resources

View File

@ -42,6 +42,7 @@ import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream
import logger
import transparentButton
import ui.Profiles.Companion.Dialogs.Companion.activateDialog
import ui.Profiles.Companion.Dialogs.Companion.deleteDialog
@ -102,9 +103,13 @@ class Profiles {
@OptIn(ExperimentalSerializationApi::class)
fun save(profile: Profile) {
val stream = FileOutputStream(getProfileLocation(profile.name))
Json.encodeToStream(profile, stream)
val json = Json { prettyPrint = true }
json.encodeToStream(profile, stream)
stream.flush()
stream.close()
logger.info { "Profile Saved: ${profile.name}.profile" }
}
/**
*

View File

@ -1,18 +1,48 @@
package ui.screens
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.TooltipArea
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.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.Button
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.FileOpen
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import ui.Profiles.Companion.Dialogs.Companion.activateDialog
import ui.Profiles.Companion.Buttons.Companion.activateButton
import ui.Profiles.Companion.Buttons.Companion.saveButton
import ui.Profiles.Companion.Buttons.Companion.deleteButton
import ui.Profiles.Companion.Dialogs.Companion.deleteDialog
import ui.Profiles.Companion.Entities.Profile
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import io.github.vinceglb.filekit.core.FileKit
import io.github.vinceglb.filekit.core.PickerMode
import io.github.vinceglb.filekit.core.PickerType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import logger
class ProfileDetails(val profile: Profile): Screen {
/**
@ -23,22 +53,156 @@ class ProfileDetails(val profile: Profile): Screen {
*
*/
@Composable
override fun Content() { //TODO: Description of what each attribute represents
Column {
Text("Name: ${profile.name}") //TODO: Text Field
Text("AI name: ${profile.aiName}") //TODO: Text Field
Text("Program name: ${profile.programName}") //TODO: Text Field
Text("Program path: ${profile.programPath}") //TODO: FileDialog
override fun Content() {
Row {
generalAttributes()
commands()
}
}
Row {
activateButton(profile)
saveButton(profile)
deleteButton(profile)
activateDialog(profile)
deleteDialog(profile)
@Composable
fun commands() {
//TODO: Display commands
}
@Composable
fun generalAttributes() {
Column {
textAttribute(
title = "Name",
tooltip = "This is the name of the profile. It also acts as the name of the saved file.",
currentValue = profile.name,
onValueChange = { newValue ->
profile.name = newValue
logger.info { "Profile Attribute Changed: name = $newValue" }
}
)
textAttribute(
title = "AI name",
tooltip = "This is the name you want to refer to the program as. It will also serve as the trigger word for commands.",
currentValue = profile.aiName,
onValueChange = { newValue ->
profile.aiName = newValue
logger.info { "Profile Attribute Changed: ai_name = $newValue" }
}
)
textAttribute(
title = "Program name",
tooltip = "This is the name of the window you want to control.",
currentValue = profile.programName,
onValueChange = { newValue ->
profile.programName = newValue
logger.info { "Profile Attribute Changed: program_name = $newValue" }
}
)
pathAttribute(
title = "Program path",
tooltip = "This is the absolute path to the program you want to control.",
currentValue = profile.programPath,
onValueChange = { newValue ->
profile.programPath = newValue
logger.info { "Profile Attribute Changed: program_path = $newValue" }
}
)
buttons()
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun pathAttribute(title: String, currentValue: String, tooltip: String, onValueChange: (String) -> Unit) {
var fieldValue by remember { mutableStateOf(currentValue) }
Row(
verticalAlignment = Alignment.CenterVertically
) {
textAttribute(
title = title,
tooltip = tooltip,
currentValue = profile.programName,
onValueChange = onValueChange
)
Button(
onClick = {
CoroutineScope(Dispatchers.IO + SupervisorJob()).async {
fieldValue = FileKit.pickFile(
title = "Pick program path",
type = PickerType.File(),
mode = PickerMode.Single,
initialDirectory = null,
)?.path.toString()
}
}
) {
Icon(
imageVector = Icons.Filled.FileOpen,
contentDescription = "Choose File"
)
}
}
//TODO: Display commands
// Allow editing. on unFocus, update profile entry
}
@Composable
fun buttons() {
Row {
activateButton(profile)
Spacer(Modifier.width(10.dp))
saveButton(profile)
Spacer(Modifier.width(10.dp))
deleteButton(profile)
activateDialog(profile)
deleteDialog(profile)
}
}
companion object {
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun textAttribute(title: String, currentValue: String, tooltip: String, onValueChange: (String) -> Unit) {
var fieldText by remember { mutableStateOf(currentValue) }
TooltipArea(
tooltip = {
Box(
modifier = Modifier
.wrapContentSize()
.background(MaterialTheme.colors.primary, CircleShape)
) {
Text(
modifier = Modifier
.padding(10.dp),
text = tooltip,
color = MaterialTheme.colors.onPrimary
)
}
}
) {
TextField(
colors = TextFieldDefaults.textFieldColors(
textColor = MaterialTheme.colors.onBackground,
backgroundColor = Color.Transparent
),
value = fieldText,
onValueChange = { newValue ->
onValueChange(newValue)
fieldText = newValue
},
label = {
Text(
text = "$title:"
)
}
)
}
}
}
}