refactor(config): niente configurazione cablata — tutto parametrizzato (v0.3.0)
Prima di pubblicare: eliminati dai sorgenti tutti i valori "cablati". - settings: default neutri (URL, voce, engine vuoti = "decide il server"); nuove chiavi ttsVoices/ttsEngines (preset dei selettori) e vadMaxMs (durata massima registrazione, prima fissa a 60 s nel codice) - ApiClient: guard su baseUrl vuoto (checkAuth/login si comportano senza rete), voce/engine omessi nella richiesta TTS se vuoti, Content-Type audio coerente col formato reale del file (ogg/wav/mp3/m4a/opus) - SettingsPage: selettori voce/engine costruiti dai preset configurabili (Repeater dentro ContextMenu = pattern provato su tide) + campi per modificare i preset; LoginPage richiede indirizzo server non vuoto - VoiceRecorder: tetto di registrazione da configurazione - README: sezione "Configuration" completa; docs neutralizzate (niente URL/valori personali: repo public-friendly)
This commit is contained in:
+83
-38
@@ -4,14 +4,10 @@ import Sailfish.Silica 1.0
|
||||
Page {
|
||||
id: page
|
||||
|
||||
property var voiceNames: [
|
||||
"it-IT-ElsaNeural",
|
||||
"it-IT-DiegoNeural",
|
||||
"it-IT-IsabellaNeural",
|
||||
"it-IT-GiuseppeMultilingualNeural",
|
||||
"en-US-AriaNeural"
|
||||
]
|
||||
property var engineNames: ["edge", "openai", "elevenlabs"]
|
||||
// Nessun elenco cablato: le voci/gli engine dei selettori vengono dalle
|
||||
// impostazioni (ttsVoices/ttsEngines) e il valore corrente è sempre
|
||||
// incluso. loading evita di salvare durante il caricamento iniziale.
|
||||
property bool loading: true
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
@@ -42,51 +38,61 @@ Page {
|
||||
|
||||
ComboBox {
|
||||
label: qsTr("Read-aloud voice")
|
||||
currentIndex: Math.max(0, page.voiceNames.indexOf(appSettings.ttsVoice))
|
||||
// MenuItems espliciti (no Repeater: pattern non provato sul kit 5.1)
|
||||
currentIndex: page.voiceIndex()
|
||||
// Repeater dentro ContextMenu = pattern provato sul device (tide)
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: "it-IT-ElsaNeural"
|
||||
onClicked: appSettings.ttsVoice = "it-IT-ElsaNeural"
|
||||
text: qsTr("(server default)")
|
||||
onClicked: appSettings.ttsVoice = ""
|
||||
}
|
||||
MenuItem {
|
||||
text: "it-IT-DiegoNeural"
|
||||
onClicked: appSettings.ttsVoice = "it-IT-DiegoNeural"
|
||||
}
|
||||
MenuItem {
|
||||
text: "it-IT-IsabellaNeural"
|
||||
onClicked: appSettings.ttsVoice = "it-IT-IsabellaNeural"
|
||||
}
|
||||
MenuItem {
|
||||
text: "it-IT-GiuseppeMultilingualNeural"
|
||||
onClicked: appSettings.ttsVoice = "it-IT-GiuseppeMultilingualNeural"
|
||||
}
|
||||
MenuItem {
|
||||
text: "en-US-AriaNeural"
|
||||
onClicked: appSettings.ttsVoice = "en-US-AriaNeural"
|
||||
Repeater {
|
||||
model: page.voiceChoices().length
|
||||
delegate: MenuItem {
|
||||
text: page.voiceChoices()[index]
|
||||
onClicked: appSettings.ttsVoice = page.voiceChoices()[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: voicesField
|
||||
width: parent.width
|
||||
label: qsTr("Voice presets (comma-separated)")
|
||||
onTextChanged: {
|
||||
if (!page.loading)
|
||||
appSettings.ttsVoices = text.split(",")
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
label: qsTr("TTS engine")
|
||||
currentIndex: Math.max(0, page.engineNames.indexOf(appSettings.ttsEngine))
|
||||
currentIndex: page.engineIndex()
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: "edge"
|
||||
onClicked: appSettings.ttsEngine = "edge"
|
||||
text: qsTr("(server default)")
|
||||
onClicked: appSettings.ttsEngine = ""
|
||||
}
|
||||
MenuItem {
|
||||
text: "openai"
|
||||
onClicked: appSettings.ttsEngine = "openai"
|
||||
}
|
||||
MenuItem {
|
||||
text: "elevenlabs"
|
||||
onClicked: appSettings.ttsEngine = "elevenlabs"
|
||||
Repeater {
|
||||
model: page.engineChoices().length
|
||||
delegate: MenuItem {
|
||||
text: page.engineChoices()[index]
|
||||
onClicked: appSettings.ttsEngine = page.engineChoices()[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: enginesField
|
||||
width: parent.width
|
||||
label: qsTr("Engine presets (comma-separated)")
|
||||
onTextChanged: {
|
||||
if (!page.loading)
|
||||
appSettings.ttsEngines = text.split(",")
|
||||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
text: qsTr("Read replies aloud")
|
||||
description: qsTr("Speak every completed reply automatically")
|
||||
@@ -159,5 +165,44 @@ Page {
|
||||
pageStack.replace(Qt.resolvedUrl("LoginPage.qml"))
|
||||
}
|
||||
|
||||
Component.onCompleted: urlField.text = appSettings.baseUrl
|
||||
// Elenchi dei selettori: preset configurati + valore corrente (così resta
|
||||
// selezionabile anche se non è tra i preset). Indice 0 = "(server default)".
|
||||
function voiceChoices() {
|
||||
var out = appSettings.ttsVoices.slice()
|
||||
var cur = appSettings.ttsVoice
|
||||
if (cur.length > 0 && out.indexOf(cur) < 0)
|
||||
out.push(cur)
|
||||
return out
|
||||
}
|
||||
|
||||
function voiceIndex() {
|
||||
var cur = appSettings.ttsVoice
|
||||
if (cur.length === 0)
|
||||
return 0
|
||||
var i = page.voiceChoices().indexOf(cur)
|
||||
return i < 0 ? 0 : i + 1
|
||||
}
|
||||
|
||||
function engineChoices() {
|
||||
var out = appSettings.ttsEngines.slice()
|
||||
var cur = appSettings.ttsEngine
|
||||
if (cur.length > 0 && out.indexOf(cur) < 0)
|
||||
out.push(cur)
|
||||
return out
|
||||
}
|
||||
|
||||
function engineIndex() {
|
||||
var cur = appSettings.ttsEngine
|
||||
if (cur.length === 0)
|
||||
return 0
|
||||
var i = page.engineChoices().indexOf(cur)
|
||||
return i < 0 ? 0 : i + 1
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
urlField.text = appSettings.baseUrl
|
||||
voicesField.text = appSettings.ttsVoices.join(", ")
|
||||
enginesField.text = appSettings.ttsEngines.join(", ")
|
||||
page.loading = false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user