* feat(web): add voice mode to task create dialog prompt
Wires the existing VoiceInputButton (introduced in #1159 for the chat
composer) into the task-create dialog's prompt toolbar so users can
dictate the initial task description.
- TaskFormInputs gains an internal insertAtCursor that splices the
transcript at the textarea caret with a single leading space when
it follows a non-whitespace character, matching the chat composer's
behaviour. A pending-cursor ref + useLayoutEffect restores the
caret position after React commits the new value.
- An optional onVoiceAutoSend prop is threaded from TaskCreateDialog
down through DialogPromptSection -> TaskFormInputs -> VoiceInputButton.
The dialog points it at formRef.current?.requestSubmit() so dictation
can create the task hands-free; every existing submit gate still
applies via the disabled submit button.
- Voice mode is gated globally on userSettings.voiceMode.enabled, so
the button self-hides for users who turned it off in settings and
self-greys on browsers without a usable engine.
* fix(web): invoke submit handler directly for voice auto-send
`HTMLFormElement.requestSubmit()` is missing on Safari < 16, so the
previous wiring silently failed with a TypeError on browsers where voice
mode itself works (Safari 14.5-15 supports Web Speech / Whisper Web).
Drop the form ref and call `setup.guardedHandleSubmit` with a synthetic
event instead. `guardedHandleSubmit` only reads `preventDefault` off the
event, and every validation gate (`validateForCreate`, session-mode
description/agent checks, edit-mode title check, `submitBlockedReason`)
lives inside the submit handler — so dictation with incomplete fields
still no-ops, identical to the prior requestSubmit-based behaviour.
This also matches the chat composer's pattern of calling the same submit
function the send button uses.
independent-review: voice auto-send Safari < 16 compat
* test(web): expand voice splice cases for task-create dialog
Adds adversarial coverage for the at-cursor splice helper that backs
voice-mode dictation in the task-create dialog:
- multi-line description with caret on a line boundary
- transcript with internal newlines preserved as-is
- existing tabs / newlines before the caret treated as whitespace
- session-mode renders the voice button
Splits the test file into a rendering-and-wiring `describe` and a
splice-behaviour `describe` so each arrow function stays under the
100-line `max-lines-per-function` lint cap.
qa: adversarial voice splice cases + session-mode parity
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>