Files
harbour-hermes/qml/pages/SettingsPage.qml
T
kaneda 9deeedf5b3 feat(voice): modalità dialogo — voce continua hands-free (v0.2.0)
- Nuovo VoiceRecorder (QAudioInput, PCM 16 bit mono): analisi del livello
  in tempo reale e stop automatico dopo il silenzio (pausa e soglia
  configurabili dalle impostazioni); produce WAV per /api/transcribe;
  emette failed("no-speech") se non rileva voce (cattura scartata)
- ChatPage: modalità dialogo (menu "Voice mode"): ascolto -> trascrizione
  -> invio -> lettura della risposta -> riascolto in ciclo; tocco sul
  testo di stato per uscire; limite di 3 errori consecutivi poi esce
- Dettatura manuale: invio automatico dopo la trascrizione (opzione,
  default attivo, disattivabile)
- Settings: sezione "Voice dialog" (auto-invio, pausa auto-stop,
  sensibilità microfono)
- Il percorso di dettatura manuale (Recorder OGG) resta invariato
2026-09-12 09:14:25 +02:00

164 lines
6.1 KiB
QML

import QtQuick 2.0
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"]
SilicaFlickable {
anchors.fill: parent
contentHeight: col.height + Theme.paddingLarge
Column {
id: col
width: parent.width
spacing: Theme.paddingMedium
PageHeader { title: qsTr("Settings") }
SectionHeader { text: qsTr("Server") }
TextField {
id: urlField
width: parent.width
label: qsTr("Web UI address")
placeholderText: "https://hermes.example.com"
inputMethodHints: Qt.ImhUrlCharactersOnly | Qt.ImhNoAutoUppercase
onTextChanged: {
if (text !== appSettings.baseUrl)
appSettings.baseUrl = text
}
}
SectionHeader { text: qsTr("Voice") }
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)
menu: ContextMenu {
MenuItem {
text: "it-IT-ElsaNeural"
onClicked: appSettings.ttsVoice = "it-IT-ElsaNeural"
}
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"
}
}
}
ComboBox {
label: qsTr("TTS engine")
currentIndex: Math.max(0, page.engineNames.indexOf(appSettings.ttsEngine))
menu: ContextMenu {
MenuItem {
text: "edge"
onClicked: appSettings.ttsEngine = "edge"
}
MenuItem {
text: "openai"
onClicked: appSettings.ttsEngine = "openai"
}
MenuItem {
text: "elevenlabs"
onClicked: appSettings.ttsEngine = "elevenlabs"
}
}
}
TextSwitch {
text: qsTr("Read replies aloud")
description: qsTr("Speak every completed reply automatically")
checked: appSettings.ttsAutoRead
onCheckedChanged: appSettings.ttsAutoRead = checked
}
SectionHeader { text: qsTr("Voice dialog") }
TextSwitch {
text: qsTr("Send right after dictation")
description: qsTr("Send the transcribed text automatically, without tapping Send")
checked: appSettings.dictationAutoSend
onCheckedChanged: appSettings.dictationAutoSend = checked
}
ComboBox {
label: qsTr("Auto-stop pause")
currentIndex: {
var v = appSettings.vadSilenceMs
return v <= 1000 ? 0 : (v <= 1500 ? 1 : (v <= 2000 ? 2 : 3))
}
menu: ContextMenu {
MenuItem { text: qsTr("1.0 second"); onClicked: appSettings.vadSilenceMs = 1000 }
MenuItem { text: qsTr("1.5 seconds"); onClicked: appSettings.vadSilenceMs = 1500 }
MenuItem { text: qsTr("2.0 seconds"); onClicked: appSettings.vadSilenceMs = 2000 }
MenuItem { text: qsTr("3.0 seconds"); onClicked: appSettings.vadSilenceMs = 3000 }
}
}
ComboBox {
label: qsTr("Mic sensitivity")
currentIndex: appSettings.vadThreshold <= 500 ? 0
: (appSettings.vadThreshold <= 1000 ? 1 : 2)
menu: ContextMenu {
MenuItem { text: qsTr("High (even soft voices)"); onClicked: appSettings.vadThreshold = 400 }
MenuItem { text: qsTr("Medium"); onClicked: appSettings.vadThreshold = 800 }
MenuItem { text: qsTr("Low (noisy rooms)"); onClicked: appSettings.vadThreshold = 1600 }
}
}
SectionHeader { text: qsTr("Account") }
Button {
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Sign out")
onClicked: page.signOut()
}
SectionHeader { text: qsTr("About") }
Label {
x: Theme.horizontalPageMargin
width: parent.width - 2 * Theme.horizontalPageMargin
wrapMode: Text.WordWrap
font.pixelSize: Theme.fontSizeSmall
color: Theme.secondaryColor
text: "harbour-hermes " + appVersion + "\n\n" +
qsTr("Native client for the Hermes Web UI (hermes-webui): " +
"sessions, live streaming chat, server-side voice " +
"dictation and read-aloud.")
}
}
VerticalScrollDecorator {}
}
function signOut() {
api.logout()
pageStack.replace(Qt.resolvedUrl("LoginPage.qml"))
}
Component.onCompleted: urlField.text = appSettings.baseUrl
}