Skip to content

MS-26473 PDChat: Voice in the input area, dictation with auto-send, agent picker - #229

Merged
davidnmbond merged 2 commits into
mainfrom
feature/MS-26473-pdchat-voice-input
Oct 5, 2026
Merged

davidnmbond merged 2 commits into
mainfrom
feature/MS-26473-pdchat-voice-input

Conversation

@davidnmbond

Copy link
Copy Markdown
Contributor

MS-26473. Follow-up to #228 (released in 10.0.322): Voice moves into the text entry area and becomes dictation, and an optional agent picker joins it.

Behaviour

  • The Voice control ("🎙️ Voice", title "Voice: speak instead of typing", aria-pressed, off by default) now sits in the input row beside Send, not in the header. Still offered only when VoiceEndpoints is not null and IsInputPermitted. The status line (Listening / Thinking / Speaking / error) is unchanged.
  • Dictation: each recognised word is appended to the text box, where it can be edited. Typing while Voice is on is allowed. A "turn" no longer sends directly; it is the pause signal. If a host reports no words, the turn text is appended instead.
  • Auto-send: after a pause, when the text box does NOT have focus, Send is pressed virtually after VoiceAutoSendDelay (same path as the button, so the message is the user's in the transcript). If the box has focus, nothing is auto-sent and the status says to press Send. A new word, focusing the box, or turning Voice off cancels a pending send.
  • While Voice is on the text box is not re-focused after a send (otherwise the next dictation would never auto-send).
  • Speaking is unchanged: the first finished reply to a message sent while Voice is listening is read aloud, half duplex, OnVoiceSpoken kept. Note: a message the user sends with the Send button while Voice is listening counts too.
  • Microphone permission: still requested only the first time Voice is turned on. pdchat-voice.js is imported and start() (the only getUserMedia) called only on that click; no probing at render. A test pins this.
  • Agent picker: a <select> ("Who you are talking to") beside Voice, shown only with 2+ agents; option tooltip from Description, icon of the selected agent from IconUrl. Choosing sets IChatService.SelectedAgentId. No model picker. No new event: a host that needs to react implements the setter, the simplest source-compatible option.

Public API (all additive)

  • IChatService.VoiceAutoSendDelay (TimeSpan, settable default member, 1000 ms via ChatServiceDefaultState)
  • IChatService.Agents (IReadOnlyList<PDChatAgentOption>?, default null)
  • IChatService.SelectedAgentId (string?, settable default member, default null)
  • record PDChatAgentOption(string Id, string Name, string? Description = null, string? IconUrl = null)
  • interface IChatInput { string Text; bool IsFocused; Task AppendAsync(string); Task SendAsync(); }, implemented by PDMessages
  • PDMessages parameters: RenderFragment? InputAccessories, EventCallback<bool> InputFocusChanged, bool IsInputAutoFocused = true

No JavaScript changed.

Verification

  • dotnet build PanoramicData.Blazor.slnx: 0 errors; the only 2 warnings are the pre-existing NuGet "packaging disabled" notices on the Web and WASM hosts (same as on main).
  • Full suite (PanoramicData.Blazor.Test.exe): 3690 passed before, 3712 passed, 0 failed, 0 skipped after (+22).
  • New/updated tests: control in the input area; dictation fills the box; a pause does not repeat dictated words; auto-send after the delay when unfocused; never auto-send when focused (then Send works); focus, a new word and Voice off each cancel a pending send; delay default 1000 ms; no microphone request until Voice is clicked; agent picker hidden with 0 and 1 agents, shown with 2, selection sets SelectedAgentId, unknown values ignored; PDMessages IChatInput members, focus tracking, accessories placement and auto-focus switch.
  • Red-check: removing the focus check from the auto-send rule made Dictation_is_never_auto_sent_while_the_text_box_has_focus fail ("Expected service.Sent to be empty, but found at least one item"); restored, it passes.
  • Not checked in a browser against a real voice host.

🤖 Generated with Claude Code

…gent picker

- Voice control moves from the header to the input row beside Send.
- Recognised words are dictated into the text box; a pause sends after
  IChatService.VoiceAutoSendDelay (default 1000 ms) unless the box has focus.
- New IChatInput abstraction, implemented by PDMessages (Text, IsFocused,
  AppendAsync, SendAsync), plus PDMessages parameters InputAccessories,
  InputFocusChanged and IsInputAutoFocused.
- Optional agent picker: IChatService.Agents and SelectedAgentId, shown with
  two or more PDChatAgentOption entries.
- The microphone is still requested only when Voice is first turned on.

All public API changes are additive; new IChatService members are default
interface members.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codacy-production

codacy-production Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 81 complexity · -4 duplication

Metric Results
Complexity 81
Duplication -4

View in Codacy

🟢 Coverage 98.85% diff coverage · +0.02% coverage variation

Metric Results
Coverage variation ✅ +0.02% coverage variation
Diff coverage ✅ 98.85% diff coverage

View coverage diff in Codacy

Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (de5791a) 15743 15727 99.90%
Head commit (2c58d34) 15829 (+86) 15816 (+89) 99.92% (+0.02%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#229) 87 86 98.85%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

AI Reviewer: run a review on demand. To trigger the first review automatically, go to your organization or repository integration settings. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

PDChatVoiceEndpoints takes an optional ModulePath replacing the browser
voice module, and PDChatVoiceEndpoints.Simulated uses one that hears that
the user is talking, makes the words up and answers with the browser's own
speech. DumbChatService offers it by default, plus two agents, so the demo
shows Voice Mode and the agent picker with no speech service.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@davidnmbond
davidnmbond merged commit 737ce57 into main Oct 5, 2026
8 checks passed
@davidnmbond
davidnmbond deleted the feature/MS-26473-pdchat-voice-input branch October 5, 2026 22:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant