Skip to content

Move the composer below the thread and show it only in text mode — the live dictation becomes the one surface above it #365

Description

@serge-ivo

The ask

"Replace the text box (at the top) with the actual message dictated, shown in realtime before being
sent — same for hands-free and push-to-talk. Move text entry to the bottom, as all messaging apps
do, and only show it in text mode."

Where the chat tab is today

┌─────────────────────────────────┐
│ textarea            ← TOP       │  :941-964   value = voice.interim || input
│ mode selector + actions         │  :966
├─────────────────────────────────┤
│ message thread (scrolls)        │
│   …messages…                    │
│   live dictation bubble         │  :1162   ← voice.dictation, LAST child
└─────────────────────────────────┘

Input above, thread below, and the live utterance at the far bottom of the thread.

The realtime display already exists

Worth stating before anyone starts building one: voice.dictation (#281) is already the realtime
dictated message. It appears as speech starts, keeps the words through the transcription round
trip rather than being overwritten by a "Transcribing…" sentinel, survives a failure with a Dismiss
affordance, and is cleared when the real message is appended. It works in both hands-free and
tap-to-talk.

So this issue is a layout change, not a new feature. The reason it reads as a missing feature is
#364: the composer still binds the dead voice.interim, so the surface the user watches shows
nothing while the working one sits off-screen.

The change

Invert the two, which is what "as all messaging apps do" resolves to:

┌─────────────────────────────────┐
│ message thread (scrolls)        │
│   …messages…                    │
│   live dictation bubble         │  ← now directly above the input
├─────────────────────────────────┤
│ mode selector + actions         │
│ textarea — TEXT MODE ONLY       │
└─────────────────────────────────┘

The dictation bubble ends up immediately above the composer without moving, because it is already the
last child of the thread. That is the whole point of the inversion: the realtime text lands where
the user is already looking, and there is exactly one surface showing it.

Decisions taken

Composer hidden outside text mode, per the ask. Consequence to accept knowingly: the tap-to-talk
placeholder currently reads "Use the voice control to talk — or type" (:951), so typing mid-voice
session is a supported escape hatch today and this removes it — the route becomes "switch to Chat".
The mode selector stays visible in every mode, so that switch is always one tap.

Two things that must move with it

Both are currently attached to the composer and would be orphaned by hiding it:

  1. The audio-level meter (:955-959) is absolutely positioned against the textarea's bottom edge
    and is the only "the mic is hearing you" signal. In voice modes its host is about to disappear. It
    belongs on the dictation bubble or the status pill — both of which are present exactly when the
    meter is relevant, which the composer no longer will be.
  2. The notice line — see [bug] Hands-free says "Listening…" and shows no words — the composer still binds voice.interim, which stopped carrying speech in #281 #364. voice.interim carries mic errors and the wrong-language
    warning through the input's value. With the input gone in voice modes, those errors have no
    surface at all, which is worse than the current confusion.

Sequence

#364 first — it is a deletion, it is the actual reported bug, and it can ship on its own. This one
then moves what remains. Doing this one first would leave the dead binding inside the restructure and
make the diff harder to read.

Not in scope

The thread's scroll anchoring already has form here (#335loadMessages scrolled unconditionally
while its siblings did not) and inverting the layout will touch the same behaviour. Worth re-checking
after, not worth pre-emptively redesigning.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfrontendFrontend / UI workvoiceVoice / STT / TTS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions