refactor BasePromptSettingsScreen to use InspiringSearchField and unify prompt settings across the app

This commit is contained in:
jonasgaudian
2026-02-13 16:54:24 +01:00
parent b5a9f5873a
commit f6fb6e77a8
10 changed files with 57 additions and 90 deletions

View File

@@ -134,7 +134,9 @@ fun AppOutlinedTextField(
fun InspiringSearchField(
value: String,
hints : Array<String>,
onValueChange: (String) -> Unit
onValueChange: (String) -> Unit,
minLines: Int = 1,
maxLines: Int = 1
) {
var currentHintIndex by remember { mutableIntStateOf(0) }
@@ -166,14 +168,14 @@ fun InspiringSearchField(
Text(
text = hint,
color = Color.Gray.copy(alpha = 0.6f),
maxLines = 1,
//maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
},
singleLine = true,
minLines = 1,
maxLines = 1
minLines = minLines,
maxLines = maxLines
)
}

View File

@@ -153,7 +153,7 @@ fun AppCard(
if (!title.isNullOrBlank()) {
Text(
text = title,
style = MaterialTheme.typography.headlineMedium,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onSurface
)
}

View File

@@ -1,7 +1,6 @@
package eu.gaudian.translator.view.settings
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -9,7 +8,6 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -32,6 +30,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import eu.gaudian.translator.R
@@ -42,7 +41,7 @@ import eu.gaudian.translator.view.composable.AppButton
import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedButton
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.InspiringSearchField
import eu.gaudian.translator.view.composable.ModelBadges
data class PromptSettingsState(
@@ -55,13 +54,14 @@ data class PromptSettingsState(
@Composable
fun BasePromptSettingsScreen(
state: PromptSettingsState,
providers: List<ApiProvider>, // Pass the list of providers
providers: List<ApiProvider>,
description: String,
onPromptChanged: (String) -> Unit,
onSaveClicked: () -> Unit,
onModelSelected: (LanguageModel?) -> Unit,
onExamplePromptClicked: (String) -> Unit
hints: Array<String>
) {
AppCard {
Column(
modifier = Modifier
.fillMaxWidth()
@@ -73,16 +73,15 @@ fun BasePromptSettingsScreen(
modifier = Modifier.padding(vertical = 16.dp)
)
AppCard(
modifier = Modifier.fillMaxWidth(),
) {
Column(Modifier.padding(16.dp)) {
AppOutlinedTextField(
InspiringSearchField(
value = state.customPrompt,
onValueChange = onPromptChanged,
modifier = Modifier.fillMaxWidth(),
label = { Text(stringResource(id = R.string.text_enter_your_custom_prompt)) },
minLines = 3
//label = { Text(stringResource(id = R.string.text_enter_your_custom_prompt)) },
minLines = 3,
maxLines = 5,
hints = hints,
)
Row(
@@ -109,26 +108,9 @@ fun BasePromptSettingsScreen(
}
}
}
}
Spacer(modifier = Modifier.height(24.dp))
Text(
text = stringResource(R.string.text_example_prompts),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.padding(bottom = 8.dp)
)
state.examplePrompts.forEach { prompt ->
Text(
text = prompt,
modifier = Modifier
.fillMaxWidth()
.clickable { onExamplePromptClicked(prompt) }
.padding(vertical = 8.dp)
)
HorizontalDivider()
}
}
}
}
@@ -274,7 +256,7 @@ fun ApiModelDropDown(
Text(
text = providerName,
style = MaterialTheme.typography.labelMedium,
fontWeight = androidx.compose.ui.text.font.FontWeight.Medium,
fontWeight = FontWeight.Medium,
color = if (isActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f)
)
Text(
@@ -305,7 +287,7 @@ fun ApiModelDropDown(
text = model.displayName,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.weight(1f),
fontWeight = if (model == selectedModel) androidx.compose.ui.text.font.FontWeight.Medium else androidx.compose.ui.text.font.FontWeight.Normal
fontWeight = if (model == selectedModel) FontWeight.Medium else FontWeight.Normal
)
Spacer(modifier = Modifier.width(8.dp))
ModelBadges(
@@ -387,5 +369,6 @@ fun BasePromptSettingsScreenPreview() {
onPromptChanged = {},
onSaveClicked = {},
onModelSelected = {},
onExamplePromptClicked = {})
hints = listOf("test1", "test2").toTypedArray(),
)
}

View File

@@ -15,6 +15,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringArrayResource
import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -82,7 +83,7 @@ fun CustomVocabularyPromptScreen(
onPromptChanged = { tempPrompt = it },
onSaveClicked = { settingsViewModel.saveCustomVocabularyPrompt(tempPrompt) },
onModelSelected = { apiViewModel.setVocabularyModel(it) },
onExamplePromptClicked = { tempPrompt = it }
hints = stringArrayResource(R.array.vocabulary_example_prompts),
)
}
}

View File

@@ -4,14 +4,12 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -34,11 +32,9 @@ import eu.gaudian.translator.utils.findActivity
import eu.gaudian.translator.view.LocalShowExperimentalFeatures
import eu.gaudian.translator.view.composable.AppCard
import eu.gaudian.translator.view.composable.AppIcons
import eu.gaudian.translator.view.composable.AppOutlinedTextField
import eu.gaudian.translator.view.composable.AppScaffold
import eu.gaudian.translator.view.composable.AppTopAppBar
import eu.gaudian.translator.view.composable.OptionItemSwitch
import eu.gaudian.translator.view.composable.PrimaryButton
import eu.gaudian.translator.view.dictionary.DictionaryManagerContent
import eu.gaudian.translator.view.hints.getDictionaryOptionsHint
import eu.gaudian.translator.viewmodel.ApiViewModel
@@ -113,51 +109,32 @@ fun DictionaryOptionsScreen(
}
item {
AppCard {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
stringResource(R.string.text_ai_model_custom_prompt),
style = MaterialTheme.typography.titleMedium
)
ApiModelDropDown(
models = availableModels,
providers = allProviders,
selectedModel = selectedModel,
onModelSelected = { model ->
apiViewModel.setDictionaryModel(model)
},
)
AppOutlinedTextField(
value = tempPrompt,
onValueChange = { tempPrompt = it },
label = { Text(stringResource(R.string.text_custom_dictionary_prompt)) },
modifier = Modifier.defaultMinSize(minHeight = 120.dp)
)
PrimaryButton(
onClick = { settingsViewModel.saveCustomPromptDictionary(tempPrompt) },
text = stringResource(R.string.text_save_prompt),
modifier = Modifier.align(Alignment.End)
)
}
}
val screenState = PromptSettingsState(
availableModels = availableModels,
selectedModel = selectedModel,
customPrompt = tempPrompt,
examplePrompts = emptyList()
)
BasePromptSettingsScreen(
state = screenState,
providers = allProviders,
description = stringResource(R.string.text_description_dictionary_prompt),
onPromptChanged = { tempPrompt = it },
onSaveClicked = { settingsViewModel.saveCustomPromptDictionary(tempPrompt) },
onModelSelected = { apiViewModel.setDictionaryModel(it) },
hints = emptyArray(), //TODO
)
}
item {
AppCard {
AppCard (
AppCard(
title = stringResource(R.string.label_dictionary_content),
text = stringResource(R.string.text_select_the_content_dictionary),
expandable = true,
initiallyExpanded = false,
){
) {
Column(Modifier.padding(0.dp)) {
dictionaryOptionKeys.zip(dictionaryOptionLabels).forEach { (key, label) ->
val isChecked = dictionarySwitches.contains(key) || dictionarySwitches.contains(label)
@@ -182,7 +159,6 @@ fun DictionaryOptionsScreen(
}
}
}
}
}
item {

View File

@@ -104,11 +104,11 @@ fun TranslationSettingsScreen(
BasePromptSettingsScreen(
state = screenState,
providers = allProviders,
description = stringResource(R.string.text_here_you_can_set),
description = stringResource(R.string.text_translation_instructions),
onPromptChanged = { tempPrompt = it },
onSaveClicked = { settingsViewModel.saveCustomPrompt(tempPrompt) },
onModelSelected = { apiViewModel.setTranslationModel(it) },
onExamplePromptClicked = { tempPrompt = it }
hints = context.resources.getStringArray(R.array.example_prompts),
)
}
}

View File

@@ -155,11 +155,9 @@
<string name="text_copy_corrected_text">Korrigierten Text kopieren</string>
<string name="label_correct">Korrekt</string>
<string name="text_explanation">Erklärung</string>
<string name="text_here_you_can_set">Hier kannst du einen benutzerdefinierten Prompt für das KI-Übersetzungsmodell festlegen, um den Übersetzungsstil anzupassen.</string>
<string name="text_vocabulary_prompt">Vokabular-Prompt</string>
<string name="text_here_you_can_set_a_custom_">Hier kannst du einen benutzerdefinierten Prompt festlegen, um zu definieren, wie neue Vokabeln erstellt werden.</string>
<string name="text_select_the_content_dictionary">Wähle die Inhalte aus, die für einen Wörterbucheintrag erstellt werden sollen.</string>
<string name="text_ai_model_custom_prompt"><![CDATA[KI-Modell & Prompt]]></string>
<string name="text_custom_dictionary_prompt">Benutzerdefinierter Wörterbuch-Prompt</string>
<string name="text_save_prompt">Prompt speichern</string>
<string name="text_light">Hell</string>

View File

@@ -153,11 +153,9 @@
<string name="text_copy_corrected_text">Copiar texto corrigido</string>
<string name="label_correct">Correto</string>
<string name="text_explanation">Explicação</string>
<string name="text_here_you_can_set">Aqui você pode definir um prompt personalizado para o modelo de tradução de IA, ajustando o estilo da tradução.</string>
<string name="text_vocabulary_prompt">Prompt de Vocabulário</string>
<string name="text_here_you_can_set_a_custom_">Aqui você pode definir um prompt personalizado para definir como novos itens de vocabulário são gerados.</string>
<string name="text_select_the_content_dictionary">Selecione o conteúdo a ser gerado para uma entrada de dicionário.</string>
<string name="text_ai_model_custom_prompt"><![CDATA[Modelo de IA & Prompt]]></string>
<string name="text_custom_dictionary_prompt">Prompt Personalizado do Dicionário</string>
<string name="text_save_prompt">Salvar Prompt</string>
<string name="text_light">Claro</string>

View File

@@ -49,12 +49,21 @@
<item>Idiomatic expressions</item>
</string-array>
<string-array name="vocabulary_example_prompts" >
<item>Use Latin American Spanish</item>
<item>Avoid long words</item>
<item>Avoid sentences</item>
<item>Include many verbs and adjectives</item>
<item>Use informal language</item>
</string-array>
<string-array name="changelog_entries">
<item>Version 0.3.0 \n• Enabled CSV Import for Vocabulary\n• Option to use a translation server for translations instead of AI models for some supported langugaes\n• UI bug fixes \n• Show word frequency \n• Performance optimizations \n• Improved translations (German and Portuguese)</item>
<item>Version 0.4.0 \n• Added dictionary download (beta) \n• UI enhancements \n• Bugfixes \n• Re-designed vocabulary card with improved UI \n• More pre-configured providers \n• Improved performance</item>
</string-array>
</resources>

View File

@@ -713,7 +713,6 @@
<string name="text_add_to_favorites">Add to favorites</string>
<string name="text_ai_failed_to_create_the_exercise">AI failed to create the exercise.</string>
<string name="text_ai_generation_failed_with_an_exception">AI generation failed with an exception</string>
<string name="text_ai_model_custom_prompt"><![CDATA[AI Model & Custom Prompt]]></string>
<string name="text_all_dictionaries_deleted_successfully">All dictionaries deleted successfully</string>
<string name="text_all_items_completed">All items completed!</string>
<string name="text_all_languages">All Languages</string>
@@ -833,7 +832,7 @@
<string name="text_generate_exercise_with_ai">Generate Exercise with AI</string>
<string name="text_generating_questions_from_video">Generating questions from video…</string>
<string name="text_get_api_key_at">Get API Key at %1$s</string>
<string name="text_here_you_can_set">Here you can set a custom prompt for the AI translation model. This allows you to fine-tune the translation style.</string>
<string name="text_translation_instructions">Set model for translation and give optional instructions on how to translate.</string>
<string name="text_here_you_can_set_a_custom_">Here you can set a custom prompt for the AI vocabulary model. This allows you to define how new vocabulary entries are generated.</string>
<string name="text_hint">Hint</string>
<string name="text_hint_you_can_search">Hint: You can search for any term, e.g. \"Things to do at the zoo\" or \"irregular verbs\"!</string>
@@ -1050,4 +1049,5 @@
<string name="label_regenerate">Regenerate</string>
<string name="label_read_aloud">Read Aloud</string>
<string name="label_all_categories">All Categories</string>
<string name="text_description_dictionary_prompt">Set a model for generating dictionary content and give optional instructions.</string>
</resources>