Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/17-keyboard-stevia-dictation.md
2026-08-10 17:00:23 -04:00

6.3 KiB

TASK 17 — Keyboard (stevia) + dictation polish

Status: shipped-partial; daily dictation use confirmed 2026-08-07. The phone went from NO on-screen keyboard to a working patched stevia with a mic key and live dictation in one session. What shipped works; the follow-ons are UX/layout polish.

What shipped (2026-07-21)

  • stevia is the OSK, patched to run under Hyprland. Upstream stevia hard-requires zphoc_device_state_v1 (phoc-private) before creating a keyboard surface, so under Hyprland it sat "Wayland not yet ready" forever and the phone had NO keyboard. Patched optional. Pixel3Arch/pkgs/stevia (0.56.0-3), builds native on-device. Squeekboard retired (SouveraineOS/blueline/squeekboard/ kept as layout reference only).
  • Mic/dictation key on every layout's bottom row (patch 0002): spawns souveraine-stt, a record→transcribe→type toggle CLI. Config (endpoint, enable) lives in Config.options.speech, surfaced in the new Settings → Speech page (souveraine 402e887), with a live /health probe. STT hits Casey's VPN faster-whisper box (see whisper-stt-server memory).
  • Terminal arrow keys (patch 0002): the terminal layout's bottom row gained ← ↓ ↑ → (the shortcuts bar always had them but sat under the pill).
  • Nav pill fix: the SystemGestureRail was lifting the pill 315px (a stale squeekboard height) which stacked it OVER the keys and stevia's upward-opening layout menu — "keyboard selection not working" was this. Now lifts to stevia's real 200px top edge; pill stays visible as the dismiss handle, clear of the keys (souveraine 5e5bf04).
  • souveraine-stt error honesty (67aeb25): distinguishes server 5xx / 4xx / unreachable from a genuine empty transcript. The old "didn't catch that" on any no-text result masked a whole server outage as a mic bug.

Follow-ons (the polish that remains)

  1. Dictation UX: the mic key spawns the CLI fire-and-forget — no in-keyboard recording indicator (the CLI sends a notify-send "Listening…" toast, which is coarse). A pressed/recording state on the key itself (stevia has --status wired: souveraine-stt --status → idle|recording) would be the honest tell. Needs a stevia patch to poll status and restyle the key, OR drive it off the notify path.

    2026-07-30 progress:

    • souveraine-stt now writes a 3-state file $XDG_RUNTIME_DIR/souveraine-stt/state (idle|recording|transcribing|error) and --status reads it (with reconciliation). notify toasts fire at every transition. Deployed on the phone.
    • Real "last word didn't type" cause found + fixed: whisper wraps output in embedded \n, and ydotool type typed \n as Enter — chopping/ submitting the dictation mid-stream. The CLI now collapses newlines→spaces (+ --key-delay) before typing.
    • stevia patch 0006 (pkgs/stevia/0006-dictate-key-status-animation.patch, Pixel3Arch e9320be, pkgrel 2): polls the state file from pos-osk-widget and swaps the dictation key's icon by state (recording = mic pulse, transcribing = content-loading, error = warning). Written, applies clean, NOT YET BUILT — archdev's aarch64 GTK3 sysroot + cross-file aren't set up where build-cross.sh expects; building 0.56.0-2 is the remaining step (then pacman -U + systemctl --user restart souveraine-stevia, verify with grim).
  2. Size: Casey settled on gsettings set mobi.phosh.osk scaling "[]" (200px, no auto-scale). The taller auto value is ['auto-portrait','auto-landscape'] (348px). Applies live, no restart. Not a bug — just record the chosen default so a reflash keeps it.

  3. Layouts: the vendored squeekboard us+apple (iOS-style) and us+hacker designs are the target feel; stevia uses its own JSON layout format (src/layouts/*.json in the stevia tree). Porting the iOS layout to stevia is open design work — start from the squeekboard header notes in SouveraineOS/blueline/squeekboard/. The target is now explicit: iOS-like letter geometry, a permanent number row, Hunspell completion retained, working language/layout switcher, mic retained, and compact arrows available outside the terminal-only layout. The parked surfaces/quickshell/assets/keyboard/us.json number-row draft is input, not acceptance.

  4. Hyprland key safety: Pixel3Arch a5fdbf6 ships stevia 0.56.0-5 with high virtual eventcodes plus zeroed fallback modifiers. Keep the compositor-collision hammer test (bound letters/digits must never dispatch Hyprland actions or shutdown) in every later layout patch.

  5. TTS: no server unblocked 2026-07-28. souveraine-speech serves POST /audio/speech at 10.10.20.123:7863 (chatterbox.cpp on ArchDev's RX 560), deliberately API-compatible with the retired VibeVoice route. Config.options.speech.tts and the Settings field are ready — this is now filling in a URL, not building a service.

Context

  • Patches: Pixel3Arch/pkgs/stevia/0001-make-zphoc-device-state-optional.patch (Hyprland compat, upstream candidate) and 0002-dictation-key-terminal-arrows.patch (mic key + arrows).
  • CLI: Pixel3Arch/rootfs-overlay/usr/local/bin/souveraine-stt.
  • Shell: souveraine/surfaces/quickshell/modules/settings/SpeechConfig.qml, modules/common/Config.qml (speech block), settings-phone.qml.
  • The OSK D-Bus plumbing (sm.puri.OSK0 SetVisible, GlobalStates.oskOpen, the "osk" IPC target) is unchanged from squeekboard — stevia's unit claims the same bus name, so shell chrome tracking needed no changes.

Acceptance

Keyboard usable in every app; mic key dictates real speech into the focused field (verified 2026-07-21: "Hello, can you hear me?" end-to-end); pill dismisses without eating key taps; number row, layout switcher, Hunspell suggestions and compact arrows work in the daily layout; a recording indicator makes the mic key's state obvious.

Connects to

whisper-stt-server (the STT backend), TASK-15 (dictation is a network fetcher the idle coordinator must wake for), the stevia patches are upstream candidates (Hyprland compat especially). Under viewtop the OSK's z-order is structurally enforced by the compositor (ad2b6cb, TASK-43) — a layer derived from the surface's furniture role, not a layerrule — so the layer-collision class this task fought in quickshell does not arise there.