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.desktop.application.dsl.TargetFormat
import org.jetbrains.compose.internal.de.undercouch.gradle.tasks.download.Download import org.jetbrains.compose.internal.de.undercouch.gradle.tasks.download.Download
import org.gradle.internal.os.OperatingSystem
plugins { plugins {
val kotlinVersion = "2.0.21" val kotlinVersion = "2.0.21"
@ -67,35 +68,53 @@ dependencies {
val serializationVersion = "1.5.1" val serializationVersion = "1.5.1"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$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 { compose.desktop {
application { application {
mainClass = "MainKt" mainClass = "MainKt"
nativeDistributions { 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" packageName = "mia"
packageVersion = "1.0.0" packageVersion = "1.0.0"
} }
} }
} }
tasks.register("buildLite") { tasks.register("packageLiteInstaller") {
group = "package project"
description = "Build program with the small vosk STT model" 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" description = "Build program with the large vosk STT model"
dependsOn("prepareFullResources", "build") dependsOn("prepareFullResources", "package")
} }
tasks.register("generateSemanticModel") { tasks.register("generateSemanticModel") {
group = "package project"
description = "Run DJL to generate Semantic model from HuggingFace (all-roberta-large-v1)" description = "Run DJL to generate Semantic model from HuggingFace (all-roberta-large-v1)"
val exeDir = ""
val modelName = "sentence-transformers/all-roberta-large-v1" val modelName = "sentence-transformers/all-roberta-large-v1"
val djlOutputDir = project.file("src/main/resources/Semantic-model") val djlOutputDir = project.file("src/main/resources/Semantic-model")
@ -106,9 +125,9 @@ tasks.register("generateSemanticModel") {
doLast { doLast {
val os = System.getProperty("os.name") val os = System.getProperty("os.name")
val executable = when { val executable = when {
os.contains("win") -> project.file("$exeDir/djl-windows.exe") currentOs.isWindows -> project.file("djl/windows.exe")
os.contains("mac") -> project.file("$exeDir/djl-mac") currentOs.isMacOsX -> project.file("djl/mac")
os.contains("linux") || os.contains("nix") || os.contains("nux") -> project.file("$exeDir/djl-linux") currentOs.isLinux -> project.file("djl/linux")
else -> throw GradleException("Unsupported OS: $os") 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") val zipFile = layout.buildDirectory.file("STT-model.zip")
tasks.register<Download>("prepareLiteResources") { tasks.register<Download>("prepareLiteResources") {
group = "package project"
description = "Download small vosk model to resources" description = "Download small vosk model to resources"
val modelName = "vosk-model-small-en-us-0.15" val modelName = "vosk-model-small-en-us-0.15"
@ -144,6 +164,7 @@ tasks.register<Download>("prepareLiteResources") {
} }
tasks.register<Download>("prepareFullResources") { tasks.register<Download>("prepareFullResources") {
group = "package project"
description = "Download large vosk model to resources" description = "Download large vosk model to resources"
val modelName = "vosk-model-en-us-0.22" val modelName = "vosk-model-en-us-0.22"

View File

@ -78,8 +78,4 @@ fun transparentButton(icon: ImageVector, contentDescription: String, onClick: ()
contentDescription = contentDescription contentDescription = contentDescription
) )
} }
} }
//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.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
import logger
import transparentButton import transparentButton
import ui.Profiles.Companion.Dialogs.Companion.activateDialog import ui.Profiles.Companion.Dialogs.Companion.activateDialog
import ui.Profiles.Companion.Dialogs.Companion.deleteDialog import ui.Profiles.Companion.Dialogs.Companion.deleteDialog
@ -102,9 +103,13 @@ class Profiles {
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
fun save(profile: Profile) { fun save(profile: Profile) {
val stream = FileOutputStream(getProfileLocation(profile.name)) val stream = FileOutputStream(getProfileLocation(profile.name))
Json.encodeToStream(profile, stream) val json = Json { prettyPrint = true }
json.encodeToStream(profile, stream)
stream.flush() stream.flush()
stream.close() stream.close()
logger.info { "Profile Saved: ${profile.name}.profile" }
} }
/** /**
* *

View File

@ -1,18 +1,48 @@
package ui.screens 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.Column
import androidx.compose.foundation.layout.Row 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.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.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import cafe.adriel.voyager.core.screen.Screen 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.Dialogs.Companion.activateDialog
import ui.Profiles.Companion.Buttons.Companion.activateButton import ui.Profiles.Companion.Buttons.Companion.activateButton
import ui.Profiles.Companion.Buttons.Companion.saveButton import ui.Profiles.Companion.Buttons.Companion.saveButton
import ui.Profiles.Companion.Buttons.Companion.deleteButton import ui.Profiles.Companion.Buttons.Companion.deleteButton
import ui.Profiles.Companion.Dialogs.Companion.deleteDialog import ui.Profiles.Companion.Dialogs.Companion.deleteDialog
import ui.Profiles.Companion.Entities.Profile 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 { class ProfileDetails(val profile: Profile): Screen {
/** /**
@ -23,22 +53,156 @@ class ProfileDetails(val profile: Profile): Screen {
* *
*/ */
@Composable @Composable
override fun Content() { //TODO: Description of what each attribute represents override fun Content() {
Column { Row {
Text("Name: ${profile.name}") //TODO: Text Field generalAttributes()
Text("AI name: ${profile.aiName}") //TODO: Text Field commands()
Text("Program name: ${profile.programName}") //TODO: Text Field }
Text("Program path: ${profile.programPath}") //TODO: FileDialog }
Row { @Composable
activateButton(profile) fun commands() {
saveButton(profile) //TODO: Display commands
deleteButton(profile) }
activateDialog(profile)
deleteDialog(profile) @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:"
)
}
)
}
}
}
}