Commit Graph
13 Commits
Author SHA1 Message Date
kaneda 7428d42b92 feat(chat): markdown formattato nella chat + testo pulito per la voce (v0.5.2)
- src/markdown.{h,cpp}: convertitore minimale, due funzioni:
  * toRichText() -> HTML per QML Text.RichText: grassetti (**x**/__x__),
    corsivi (*x*/_x_ con confini di parola per non toccare snake_case),
    titoli (#), liste (-/1.), citazioni (>), codice (`x` e blocchi ```,
    in monospace), link [t](u) -> solo testo, barrato ~~x~~; escape di
    < & > contro injection nel rich text
  * toSpeech() -> testo piatto per il TTS: via recinti di codice, marcatori
    di titoli/liste/citazioni, enfasi, backtick; link/immagini -> testo;
    tabelle | a | b | -> "a, b"; righe vuote compattate
- ChatModel: nuovo ruolo contentRich (calcolato in C++, testabile);
  incluso nei dataChanged dello streaming
- ChatPage: le bolle dell'assistente usano contentRich con
  textFormat: Text.RichText; i messaggi utente restano testo semplice
- ApiClient::speak(): il testo passa da Markdown::toSpeech (niente più
  simboli di formattazione letti ad alta voce)
- core-test: 19 casi nuovi per il convertitore (tutti locali, senza device)

Verificato: build zero warning + unit test completi.
2026-09-13 08:04:19 +02:00
kaneda 257f9a079e docs(protocol): nota prestazioni — primo fetch sessioni dopo switch profilo (10-16 s, cache fredde) 2026-09-13 07:37:40 +02:00
kaneda c5c4049030 fix(api): watchdog con timeout su tutte le richieste — mai piu' attese infinite (v0.5.1)
Sintomo sul device: l'app restava sullo splash in caricamento per sempre.
Causa: QNetworkAccessManager non applica timeout — una richiesta che non
riceve risposta (rete del telefono, server occupato) lasciava la UI in
attesa infinita, senza errori visibili.

- ApiClient::armTimeout(): watchdog per reply (abort -> normale percorso
  d'errore del chiamante). Applicato a: checkAuth (12 s), login (20 s),
  refreshSessions e openSession (60 s), newSession/chat-start/switchProfile
  (20-30 s), fetchProfiles (20 s), transcribe (300 s), tts (120 s).
  Nessun watchdog sullo stream SSE (longevo per design).
- checkAuth: imposta lastError anche in errore (messaggio tradotto
  "Server not reachable (timeout)") e logga indirizzo ed esito
- refreshSessions: log della durata (misurato sul probe: il primo fetch
  dopo un cambio profilo puo' prendere 11-16 s lato server, anche su DB
  minuscolo — l'app ora lo gestisce senza sembrare bloccata)
- SplashPage: rete di sicurezza (se authChecked non arriva entro 15 s si
  va comunque al login) + log di instradamento
- i18n: nuova stringa tradotta it/fr/de
2026-09-13 07:37:12 +02:00
kaneda 7b8e5e1102 feat(profile): selezione del profilo di cui vedere le sessioni (v0.5.0)
- Impostazioni > Sessions: selettore "Profile" (elenco da /api/profiles;
  "(server active)" come prima voce = nessun cookie di profilo)
- ApiClient: fetchProfiles() + switchProfile(); lo switch usa
  /api/profile/switch (process_wide=False lato server: il cookie firmato
  hermes_profile scopa le richieste dell'app per-client; il profilo attivo
  globale del server non viene toccato); refresh automatico delle sessioni
- CookieJar::removeCookiesByName per l'opzione "(server active)"
- Il profilo scelto viene riapplicato automaticamente dopo il login
- i18n: nuove stringhe tradotte it/fr/de
2026-09-12 22:46:27 +02:00
kaneda 22ccb49424 feat(i18n): localizzazione interfaccia — italiano, francese, tedesco (v0.4.0)
- translations/: harbour-hermes-it/fr/de.ts (78 stringhe ciascuna, 100%
  tradotte) + caricamento in main.cpp in base alla lingua di sistema
- .pro: CONFIG += sailfishapp_i18n + TRANSLATIONS (pattern ufficiale
  Sailfish: .qm generati in build e installati in
  /usr/share/harbour-hermes/translations)
- stringhe visibili del core in tr(): ApiClient, ChatModel, Recorder
  (VoiceRecorder era gia' ok); plurali dei messaggi corretti
  (1 message / %1 messages) in SessionsPage e CoverPage
- spec: BuildRequires qt5-qttools-linguist + %files translations
- README/BUILD: sezione Localization + workflow lupdate/lrelease

Verificato: lupdate 78/78 per it/fr/de, lrelease OK, e test runtime di
caricamento .qm (tutte le traduzioni risolte nei contesti corretti).
2026-09-12 22:37:32 +02:00
kaneda 48467b372d docs(readme): nota sui limiti delle voci TTS imposti dal server (v0.3.2)
Le voci selezionabili per la lettura ad alta voce dipendono dall'allowlist
Edge TTS del server hermes-webui: se il server rifiuta una voce (400
invalid voice) si sceglie tra quelle ammesse, si lascia vuoto (default
del server) o si estende l'allowlist sul proprio server. Solo
documentazione: nessuna modifica al comportamento dell'app.
2026-09-12 22:28:41 +02:00
kaneda fa2aafa9dd fix(autosend): invio dettatura robusto e visibile (v0.3.1)
L'auto-invio poteva non scattare senza alcun segnale. Cause eliminate:
- il testo viene passato esplicitamente (sendText) invece di rileggerlo
  dal campo; il focus tastiera non viene piu' forzato prima dell'invio
- l'opzione vale "on" salvo disattivazione esplicita (undefined -> on)
- ChatModel::send non rifiuta piu' in silenzio: avviso a schermo
  ("No session open" / "Wait for the current reply") + log [chat]
- log [hermes] sul percorso trascrizione/auto-invio per la diagnosi
  dal terminale
2026-09-12 13:49:20 +02:00
kaneda a5932f0025 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)
2026-09-12 12:42:06 +02:00
kaneda 558a384339 fix(build): silenziato -Wreorder nel costruttore di ApiClient (v0.2.1)
L'inizializzazione di m_sessionsModel era in lista prima dei flag bool,
ma il membro e' dichiarato dopo: gcc avvisava con -Wreorder. Spostata
l'entry in coda, dopo m_transcribing. Build completa senza warning.
Nessun cambio di comportamento (la 0.2.0 compilata e' identica).
2026-09-12 09:23:59 +02:00
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
kaneda 479edb0f99 fix(sessions): lista vuota sul device — modello C++ con ruoli (v0.1.2)
Causa: su Qt 5.6 (kit Sailfish 5.1) una QVariantList di QVariantMap
usata come model non fa risolvere i campi nei delegate QML
(model.title/message_count/updated_at = undefined -> righe vuote), pur
con payload corretto dal server (verificato con cattura reale del 12/09).

- Nuovo src/sessionsmodel.{h,cpp}: QAbstractListModel con ruoli
  session_id/title/message_count/updated_at; api.sessions ora e' il
  modello, api.sessionsCount per lo stato "nessuna sessione"
- /api/sessions con sidebar_source=webui&exclude_hidden=1 (come la
  sidebar del browser: solo sessioni WebUI, niente CLI/cron/nascoste)
- SessionsPage: describe(message_count, updated_at) con parametri espliciti
- PROTOCOL.md: payload sessions verificato + nota del pitfall Qt 5.6
2026-09-12 08:47:28 +02:00
kaneda 98139be96a 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")
2026-09-12 08:09:30 +02:00
kaneda 9c6a1242f7 feat: v0.1.0 — client Sailfish per hermes-webui con voce
- ApiClient HTTP/SSE: login, sessioni, chat/start, stream SSE, transcribe, tts
- ChatModel: streaming live + risincronizzazione dallo stato del server
- CookieJar persistente (0600, mai la password); Settings
- Recorder OGG/Opus (fallback WAV/PCM, retry); Player TTS (QMediaPlayer)
- UI Silica: splash, login, sessioni, chat (dettatura + read-aloud), settings, cover
- Sailjail Internet;Audio;Microphone; icone; spec RPM; docs PROTOCOL/BUILD
- tests/core_test: unit parser SSE + integrazione live (probe :8899)
2026-09-12 07:11:20 +02:00