diff --git a/README.md b/README.md index 1386894..85592e7 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,6 @@ speaks (validated live against a real instance). ## Status -Version 0.1.0 — protocol and C++ core validated against a real `hermes-webui` +Version 0.1.1 — protocol and C++ core validated against a real `hermes-webui` (login, sessions, streaming chat, TTS, STT); on-device testing (audio paths, Sailjail prompts, keyboard handling) still pending. diff --git a/docs/BUILD.md b/docs/BUILD.md index 1b0759d..72b6c62 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -58,16 +58,30 @@ Nota desktop: se manca `libGL.so` di sviluppo, creare un symlink `core_test.pro` punti lì (`GLSTUB`), oppure lanciare con `LD_LIBRARY_PATH` puntato alla lib dir di Qt. +Nota desktop 2: Qt5Multimedia richiede `libpulse-mainloop-glib.so.0` +(pacchetto `libpulse-mainloop-glib0`, non installato su questo server). +Senza sudo si estrae dal .deb: + +```bash +mkdir -p /tmp/hh-libs && cd /tmp/hh-libs +apt-get download libpulse-mainloop-glib0 +dpkg-deb -x libpulse-mainloop-glib0_*.deb ext +export LD_LIBRARY_PATH=/tmp/hh-libs/ext/usr/lib/x86_64-linux-gnu:/gcc_64/lib +``` + +Esecuzione: `./core-test` (unit) e `./core-test --live` (integrazione, +richiede il probe WebUI su :8899). Esito 12/09/2026: unit 9/9, live completo OK. + ## 4. Tarball per la build da Qt Creator ```bash cd /home/kaneda/workspace -tar czf harbour-hermes-0.1.0.tar.gz \ - --transform 's,^harbour-hermes,harbour-hermes-0.1.0,' \ +tar czf harbour-hermes-0.1.1.tar.gz \ + --transform 's,^harbour-hermes,harbour-hermes-0.1.1,' \ --exclude='.git' harbour-hermes ``` -Lo spec si aspetta la directory `harbour-hermes-0.1.0/` (pattern degli altri +Lo spec si aspetta la directory `harbour-hermes-0.1.1/` (pattern degli altri progetti: cercato in modo robusto anche per i sorgenti live di sfdk). ## 5. Dipendenze lato server (WebUI) diff --git a/qml/pages/ChatPage.qml b/qml/pages/ChatPage.qml index e756c40..d343421 100644 --- a/qml/pages/ChatPage.qml +++ b/qml/pages/ChatPage.qml @@ -55,21 +55,21 @@ Page { } MenuItem { text: appSettings.ttsAutoRead ? qsTr("Auto-read: on") : qsTr("Auto-read: off") - onClicked: appSettings.ttsAutoRead = !appSettings.ttsAutoRead + onClicked: page.toggleAutoRead() } MenuItem { text: qsTr("Stop playback") visible: player.playing - onClicked: player.stop() + onClicked: page.stopPlayback() } MenuItem { text: qsTr("Cancel run") visible: chat.busy - onClicked: chat.cancel() + onClicked: page.cancelRun() } MenuItem { text: qsTr("Resync") - onClicked: chat.resync() + onClicked: page.resync() } } @@ -111,7 +111,7 @@ Page { anchors.bottom: parent.bottom anchors.bottomMargin: page.kbHeight height: compCol.height + Theme.paddingMedium - color: Theme.highlightDimmerColor + color: Theme.rgba(Theme.highlightBackgroundColor, 0.15) Column { id: compCol @@ -141,7 +141,7 @@ Page { Button { id: micBtn text: recorder.recording ? qsTr("Stop") : qsTr("Mic") - highlighted: recorder.recording + // Button non ha "highlighted": lo stato è nel testo (Mic/Stop) enabled: !api.transcribing && !chat.busy || recorder.recording onClicked: { if (recorder.recording) @@ -157,7 +157,9 @@ Page { id: input width: parent.width - micBtn.width - sendBtn.width - 2 * parent.spacing placeholderText: qsTr("Message…") - onAccepted: page.doSend() + // Invio con EnterKey (vedi LoginPage) + EnterKey.enabled: text.length > 0 + EnterKey.onClicked: page.doSend() } Button { @@ -226,6 +228,22 @@ Page { api.speak(t) } + function toggleAutoRead() { + appSettings.ttsAutoRead = !appSettings.ttsAutoRead + } + + function stopPlayback() { + player.stop() + } + + function cancelRun() { + chat.cancel() + } + + function resync() { + chat.resync() + } + function showNotice(text) { page.noticeText = text noticeClear.restart() diff --git a/qml/pages/LoginPage.qml b/qml/pages/LoginPage.qml index eff02bc..6db3f4d 100644 --- a/qml/pages/LoginPage.qml +++ b/qml/pages/LoginPage.qml @@ -36,7 +36,10 @@ Page { label: qsTr("Password") echoMode: TextInput.Password inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText - onAccepted: page.doLogin() + // Silica TextField NON ha il segnale "accepted": l'invio si gestisce + // con la proprietà allegata EnterKey (pattern provato sul device). + EnterKey.enabled: text.length > 0 + EnterKey.onClicked: page.doLogin() } Button { diff --git a/qml/pages/SessionsPage.qml b/qml/pages/SessionsPage.qml index c2f5907..9ec0d51 100644 --- a/qml/pages/SessionsPage.qml +++ b/qml/pages/SessionsPage.qml @@ -24,15 +24,15 @@ Page { } MenuItem { text: qsTr("Refresh") - onClicked: api.refreshSessions() + onClicked: page.refresh() } MenuItem { text: qsTr("Settings") - onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) + onClicked: page.openSettings() } MenuItem { text: qsTr("Sign out") - onClicked: api.logout() + onClicked: page.signOut() } } @@ -51,7 +51,7 @@ Page { width: parent.width truncationMode: TruncationMode.Fade color: item.highlighted ? Theme.highlightColor : Theme.primaryColor - text: ("" + model.title).length > 0 ? model.title : model.session_id + text: model.title ? model.title : model.session_id } Label { @@ -97,6 +97,18 @@ Page { Component.onCompleted: api.refreshSessions() + function refresh() { + api.refreshSessions() + } + + function openSettings() { + pageStack.push(Qt.resolvedUrl("SettingsPage.qml")) + } + + function signOut() { + api.logout() + } + function openSession(id) { expectNewSession = false pageStack.push(Qt.resolvedUrl("ChatPage.qml"), { sessionId: id }) diff --git a/qml/pages/SettingsPage.qml b/qml/pages/SettingsPage.qml index f585ba0..d9e1e56 100644 --- a/qml/pages/SettingsPage.qml +++ b/qml/pages/SettingsPage.qml @@ -43,13 +43,27 @@ 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) menu: ContextMenu { - Repeater { - model: page.voiceNames - MenuItem { - text: modelData - onClicked: appSettings.ttsVoice = modelData - } + 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" } } } @@ -58,12 +72,17 @@ Page { label: qsTr("TTS engine") currentIndex: Math.max(0, page.engineNames.indexOf(appSettings.ttsEngine)) menu: ContextMenu { - Repeater { - model: page.engineNames - MenuItem { - text: modelData - onClicked: appSettings.ttsEngine = modelData - } + MenuItem { + text: "edge" + onClicked: appSettings.ttsEngine = "edge" + } + MenuItem { + text: "openai" + onClicked: appSettings.ttsEngine = "openai" + } + MenuItem { + text: "elevenlabs" + onClicked: appSettings.ttsEngine = "elevenlabs" } } } @@ -80,10 +99,7 @@ Page { Button { anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Sign out") - onClicked: { - api.logout() - pageStack.replace(Qt.resolvedUrl("LoginPage.qml")) - } + onClicked: page.signOut() } SectionHeader { text: qsTr("About") } @@ -104,5 +120,10 @@ Page { VerticalScrollDecorator {} } + function signOut() { + api.logout() + pageStack.replace(Qt.resolvedUrl("LoginPage.qml")) + } + Component.onCompleted: urlField.text = appSettings.baseUrl } diff --git a/rpm/harbour-hermes.spec b/rpm/harbour-hermes.spec index 2fe4a87..bf117cf 100644 --- a/rpm/harbour-hermes.spec +++ b/rpm/harbour-hermes.spec @@ -1,6 +1,6 @@ Name: harbour-hermes Summary: Client for the Hermes Web UI with voice -Version: 0.1.0 +Version: 0.1.1 Release: 1 Group: Qt/Qt License: MIT @@ -24,6 +24,10 @@ Sessions list, chat with live streaming replies, voice dictation (server-side TTS), plus a hands-free conversation mode. %changelog +* Sat Sep 12 2026 Carlo Baratto - 0.1.1-1 +- Fix pagine non caricabili sul device: TextField.onAccepted inesistente + -> EnterKey; Button.highlighted rimosso; ComboBox senza Repeater; + Theme.highlightDimmerColor -> Theme.rgba; azioni menu via Page * Sat Sep 12 2026 Carlo Baratto - 0.1.0-1 - Prima release: login alla Web UI di Hermes, lista sessioni, chat con streaming SSE, dettatura vocale (registrazione OGG/Opus