fix(qml): correzioni Silica post-primo-test su device (v0.1.1)
Pagine non caricabili ("Impossibile caricare la pagina") per API
inesistenti nella Silica del kit Sailfish 5.1 / Qt 5.6 — verificate
sui docs ufficiali (pagine -members) e incrociate coi pattern
provati su device (tide/calibreweb/quill):
- TextField: rimosso onAccepted (segnale "accepted" inesistente)
-> EnterKey.enabled/.onClicked (LoginPage, ChatPage)
- Button: rimosso "highlighted" (inesistente su Button) dal mic
- ComboBox: menu con MenuItem espliciti (via Repeater non provato)
- Theme.highlightDimmerColor -> Theme.rgba(highlightBackgroundColor)
- Azioni nei menu instradate via funzioni della Page (pattern tide)
- SessionsPage: titolo delegato piu' robusto (no "undefined")
This commit is contained in:
@@ -29,6 +29,6 @@ speaks (validated live against a real instance).
|
|||||||
|
|
||||||
## Status
|
## 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,
|
(login, sessions, streaming chat, TTS, STT); on-device testing (audio paths,
|
||||||
Sailjail prompts, keyboard handling) still pending.
|
Sailjail prompts, keyboard handling) still pending.
|
||||||
|
|||||||
+17
-3
@@ -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`
|
`core_test.pro` punti lì (`GLSTUB`), oppure lanciare con `LD_LIBRARY_PATH`
|
||||||
puntato alla lib dir di Qt.
|
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:<Qt>/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
|
## 4. Tarball per la build da Qt Creator
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /home/kaneda/workspace
|
cd /home/kaneda/workspace
|
||||||
tar czf harbour-hermes-0.1.0.tar.gz \
|
tar czf harbour-hermes-0.1.1.tar.gz \
|
||||||
--transform 's,^harbour-hermes,harbour-hermes-0.1.0,' \
|
--transform 's,^harbour-hermes,harbour-hermes-0.1.1,' \
|
||||||
--exclude='.git' harbour-hermes
|
--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).
|
progetti: cercato in modo robusto anche per i sorgenti live di sfdk).
|
||||||
|
|
||||||
## 5. Dipendenze lato server (WebUI)
|
## 5. Dipendenze lato server (WebUI)
|
||||||
|
|||||||
+25
-7
@@ -55,21 +55,21 @@ Page {
|
|||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: appSettings.ttsAutoRead ? qsTr("Auto-read: on") : qsTr("Auto-read: off")
|
text: appSettings.ttsAutoRead ? qsTr("Auto-read: on") : qsTr("Auto-read: off")
|
||||||
onClicked: appSettings.ttsAutoRead = !appSettings.ttsAutoRead
|
onClicked: page.toggleAutoRead()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Stop playback")
|
text: qsTr("Stop playback")
|
||||||
visible: player.playing
|
visible: player.playing
|
||||||
onClicked: player.stop()
|
onClicked: page.stopPlayback()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Cancel run")
|
text: qsTr("Cancel run")
|
||||||
visible: chat.busy
|
visible: chat.busy
|
||||||
onClicked: chat.cancel()
|
onClicked: page.cancelRun()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Resync")
|
text: qsTr("Resync")
|
||||||
onClicked: chat.resync()
|
onClicked: page.resync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ Page {
|
|||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.bottomMargin: page.kbHeight
|
anchors.bottomMargin: page.kbHeight
|
||||||
height: compCol.height + Theme.paddingMedium
|
height: compCol.height + Theme.paddingMedium
|
||||||
color: Theme.highlightDimmerColor
|
color: Theme.rgba(Theme.highlightBackgroundColor, 0.15)
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: compCol
|
id: compCol
|
||||||
@@ -141,7 +141,7 @@ Page {
|
|||||||
Button {
|
Button {
|
||||||
id: micBtn
|
id: micBtn
|
||||||
text: recorder.recording ? qsTr("Stop") : qsTr("Mic")
|
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
|
enabled: !api.transcribing && !chat.busy || recorder.recording
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (recorder.recording)
|
if (recorder.recording)
|
||||||
@@ -157,7 +157,9 @@ Page {
|
|||||||
id: input
|
id: input
|
||||||
width: parent.width - micBtn.width - sendBtn.width - 2 * parent.spacing
|
width: parent.width - micBtn.width - sendBtn.width - 2 * parent.spacing
|
||||||
placeholderText: qsTr("Message…")
|
placeholderText: qsTr("Message…")
|
||||||
onAccepted: page.doSend()
|
// Invio con EnterKey (vedi LoginPage)
|
||||||
|
EnterKey.enabled: text.length > 0
|
||||||
|
EnterKey.onClicked: page.doSend()
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
@@ -226,6 +228,22 @@ Page {
|
|||||||
api.speak(t)
|
api.speak(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleAutoRead() {
|
||||||
|
appSettings.ttsAutoRead = !appSettings.ttsAutoRead
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPlayback() {
|
||||||
|
player.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelRun() {
|
||||||
|
chat.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
function resync() {
|
||||||
|
chat.resync()
|
||||||
|
}
|
||||||
|
|
||||||
function showNotice(text) {
|
function showNotice(text) {
|
||||||
page.noticeText = text
|
page.noticeText = text
|
||||||
noticeClear.restart()
|
noticeClear.restart()
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ Page {
|
|||||||
label: qsTr("Password")
|
label: qsTr("Password")
|
||||||
echoMode: TextInput.Password
|
echoMode: TextInput.Password
|
||||||
inputMethodHints: Qt.ImhNoAutoUppercase | Qt.ImhNoPredictiveText
|
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 {
|
Button {
|
||||||
|
|||||||
@@ -24,15 +24,15 @@ Page {
|
|||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Refresh")
|
text: qsTr("Refresh")
|
||||||
onClicked: api.refreshSessions()
|
onClicked: page.refresh()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Settings")
|
text: qsTr("Settings")
|
||||||
onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
|
onClicked: page.openSettings()
|
||||||
}
|
}
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: qsTr("Sign out")
|
text: qsTr("Sign out")
|
||||||
onClicked: api.logout()
|
onClicked: page.signOut()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Page {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
truncationMode: TruncationMode.Fade
|
truncationMode: TruncationMode.Fade
|
||||||
color: item.highlighted ? Theme.highlightColor : Theme.primaryColor
|
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 {
|
Label {
|
||||||
@@ -97,6 +97,18 @@ Page {
|
|||||||
|
|
||||||
Component.onCompleted: api.refreshSessions()
|
Component.onCompleted: api.refreshSessions()
|
||||||
|
|
||||||
|
function refresh() {
|
||||||
|
api.refreshSessions()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSettings() {
|
||||||
|
pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
|
||||||
|
}
|
||||||
|
|
||||||
|
function signOut() {
|
||||||
|
api.logout()
|
||||||
|
}
|
||||||
|
|
||||||
function openSession(id) {
|
function openSession(id) {
|
||||||
expectNewSession = false
|
expectNewSession = false
|
||||||
pageStack.push(Qt.resolvedUrl("ChatPage.qml"), { sessionId: id })
|
pageStack.push(Qt.resolvedUrl("ChatPage.qml"), { sessionId: id })
|
||||||
|
|||||||
+33
-12
@@ -43,13 +43,27 @@ Page {
|
|||||||
ComboBox {
|
ComboBox {
|
||||||
label: qsTr("Read-aloud voice")
|
label: qsTr("Read-aloud voice")
|
||||||
currentIndex: Math.max(0, page.voiceNames.indexOf(appSettings.ttsVoice))
|
currentIndex: Math.max(0, page.voiceNames.indexOf(appSettings.ttsVoice))
|
||||||
|
// MenuItems espliciti (no Repeater: pattern non provato sul kit 5.1)
|
||||||
menu: ContextMenu {
|
menu: ContextMenu {
|
||||||
Repeater {
|
|
||||||
model: page.voiceNames
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: modelData
|
text: "it-IT-ElsaNeural"
|
||||||
onClicked: appSettings.ttsVoice = modelData
|
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")
|
label: qsTr("TTS engine")
|
||||||
currentIndex: Math.max(0, page.engineNames.indexOf(appSettings.ttsEngine))
|
currentIndex: Math.max(0, page.engineNames.indexOf(appSettings.ttsEngine))
|
||||||
menu: ContextMenu {
|
menu: ContextMenu {
|
||||||
Repeater {
|
|
||||||
model: page.engineNames
|
|
||||||
MenuItem {
|
MenuItem {
|
||||||
text: modelData
|
text: "edge"
|
||||||
onClicked: appSettings.ttsEngine = modelData
|
onClicked: appSettings.ttsEngine = "edge"
|
||||||
}
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: "openai"
|
||||||
|
onClicked: appSettings.ttsEngine = "openai"
|
||||||
|
}
|
||||||
|
MenuItem {
|
||||||
|
text: "elevenlabs"
|
||||||
|
onClicked: appSettings.ttsEngine = "elevenlabs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,10 +99,7 @@ Page {
|
|||||||
Button {
|
Button {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: qsTr("Sign out")
|
text: qsTr("Sign out")
|
||||||
onClicked: {
|
onClicked: page.signOut()
|
||||||
api.logout()
|
|
||||||
pageStack.replace(Qt.resolvedUrl("LoginPage.qml"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SectionHeader { text: qsTr("About") }
|
SectionHeader { text: qsTr("About") }
|
||||||
@@ -104,5 +120,10 @@ Page {
|
|||||||
VerticalScrollDecorator {}
|
VerticalScrollDecorator {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function signOut() {
|
||||||
|
api.logout()
|
||||||
|
pageStack.replace(Qt.resolvedUrl("LoginPage.qml"))
|
||||||
|
}
|
||||||
|
|
||||||
Component.onCompleted: urlField.text = appSettings.baseUrl
|
Component.onCompleted: urlField.text = appSettings.baseUrl
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: harbour-hermes
|
Name: harbour-hermes
|
||||||
Summary: Client for the Hermes Web UI with voice
|
Summary: Client for the Hermes Web UI with voice
|
||||||
Version: 0.1.0
|
Version: 0.1.1
|
||||||
Release: 1
|
Release: 1
|
||||||
Group: Qt/Qt
|
Group: Qt/Qt
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -24,6 +24,10 @@ Sessions list, chat with live streaming replies, voice dictation
|
|||||||
(server-side TTS), plus a hands-free conversation mode.
|
(server-side TTS), plus a hands-free conversation mode.
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sat Sep 12 2026 Carlo Baratto <carlo@carlobaratto.it> - 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 <carlo@carlobaratto.it> - 0.1.0-1
|
* Sat Sep 12 2026 Carlo Baratto <carlo@carlobaratto.it> - 0.1.0-1
|
||||||
- Prima release: login alla Web UI di Hermes, lista sessioni,
|
- Prima release: login alla Web UI di Hermes, lista sessioni,
|
||||||
chat con streaming SSE, dettatura vocale (registrazione OGG/Opus
|
chat con streaming SSE, dettatura vocale (registrazione OGG/Opus
|
||||||
|
|||||||
Reference in New Issue
Block a user