7.6 KiB
7.6 KiB
APP-4717 — Enter on empty input sends the top queued prompt
See specs/APP-4717/PRODUCT.md for behavior. Researched at commit e367c9de8b9629600885e40b029c10c8915f9ec8.
Context
app/src/terminal/input.rs:12808 @ e367c9de—Input::input_enter. CLI-agent rich input returns early at the top (L12809-12867), so the queue-send path never applies there (PRODUCT §10). The else-if chain at L12984-12988 (maybe_launch_cloud_handoff_request/maybe_queue_input_for_in_progress_conversation/ …) is where the new empty-buffer check slots in; all existing branches in that chain require a non-empty buffer, so ordering is conflict-free.app/src/terminal/input.rs:3755-3793 @ e367c9de—handle_queued_prompts_panel_event: the existing Send-now dispatch (command vs prompt,remove_fired_row, refocus). This is the logic Enter must reuse.app/src/terminal/view/queued_prompts_panel.rs:580-620 @ e367c9de—SendNowaction handler; skips rows whererow.is_locked()(the initial cloud-mode prompt), which is exactly the head-row sendability condition (update_send_now_availability, L285-324, disables the head only when it is the locked initial cloud-mode row).app/src/terminal/view/queued_prompts_panel.rs:853-903 @ e367c9de—render_header("N queued" label) where the "⏎ to send" hint goes.should_render(L548-563) already gates on flag, inline menus, and queue presence.app/src/terminal/input.rs:9756-9763 @ e367c9de—Inputalready detects empty↔non-empty buffer transitions on everyEditedevent (is_editor_empty_on_last_edit); the panel can be driven from here.app/src/server/telemetry/events.rs:2945-2963 @ e367c9de— existingQueuedPrompt*telemetry events to extend.
Proposed changes
- Shared dispatch helper on
Input(app/src/terminal/input.rs): extract the body of theQueuedPromptsPanelEvent::SendNowarm intofn send_queued_row_immediately(&mut self, conversation_id, query_id, text, is_command, trigger, ctx). Both the panel-event arm and the Enter path call it. It emits the new telemetry event (below) on dispatch. - Panel send state (
app/src/terminal/view/queued_prompts_panel.rs):can_send_prompt: bool(host-pushed via the change-detectingset_can_send_promptsetter) — whether the terminal can send prompts at all (false for read-only shared-session viewers, via the existingSharedSessionStatus::is_reader()helper). Gates the Send-now buttons (re-runsupdate_send_now_availability, with a "Read-only viewers cannot send prompts." tooltip), empty-Enter sends, and the hint. Edit/delete buttons are unaffected.- Host-input emptiness is not pushed: the panel holds the host editor's
ViewHandle(passed at construction) and readsis_emptylive at decision time, so the Enter decision cannot trail same-update buffer changes. A subscription to the host editor'sEdited/BufferReplacedevents re-renders the panel on empty↔non-empty transitions (a cachedhost_editor_was_emptyflag only damps these notifications), mirroring theCLIAgentSessionsModelpattern below. The panel observes the CLI-agent rich input itself (aCLIAgentSessionsModelsubscription plus a liveis_input_openread — Enter submits to the CLI agent while it is open) and exposesenter_sends_queued_prompt(ctx)=should_render+can_send_prompt+ live editor emptiness + rich input closed. The hint shows when that holds, no row is in inline edit mode, and the head row is sendable (!is_locked()), so the hint can never advertise an Enter that wouldn't fire.
- Enter path: inlined in
input_enter's dispatch chain (beforemaybe_launch_cloud_handoff_request): whenpanel.enter_sends_queued_prompt(ctx)holds, look up the head row of the active conversation's queue (BlocklistAIHistoryModel::active_conversation_id+QueuedQueryModel::queue(...).first(), skipping a locked head) and dispatch it viasend_queued_row_immediately; a locked head means Enter does nothing. - Push sites:
Inputseedscan_send_promptat panel construction (is_reader) and passes the editor handle in;TerminalView::on_self_role_updatedpushesset_can_send_prompt(role.can_execute())when the shared-session role changes (panel reached via theInput::queued_prompts_panel()accessor). - Header hint rendering:
render_headerappends an enter keycap chip (render_keystroke_with_color_overrides, the same component the "? for help" message-bar hints use) followed by "to send" text. The text uses the header'ssub_text_color; the keycap glyph usesinternal_colors::text_disabledso it is dimmer. Spacing follows the message-bar hint rules (render_message_bar_items): 8px label→keycap, 4px keycap→text. - Telemetry (
app/src/server/telemetry/events.rs): new eventQueuedPromptSentNow { origin: TelemetryQueuedQueryOrigin, trigger: QueuedPromptSendNowTrigger }withQueuedPromptSendNowTrigger { SendNowButton, EnterOnEmptyInput }, payload + descriptions following the adjacentQueuedPrompt*events. Emitted from the shared helper in (1). (Send-now currently has no telemetry; this adds it for both triggers.)
No new feature flag: the behavior ships under the existing QueueSlashCommand gate the panel already requires.
Testing and validation
- Unit tests in
app/src/terminal/input_tests.rsnext to the existing queued-panel host tests (L1277+), drivinginput_enter:- empty buffer + queued prompt row → head row dispatched, removed from queue, buffer untouched (PRODUCT §1, §11); a second Enter sends the next row (§3).
- empty buffer + queued command row, default shell mode → command executed instead of an empty shell submission (§1, §2).
- non-empty buffer → no queue send (§6).
- locked initial cloud-mode head row → no send (§5) — the
!is_lockedfilter in the Enter path is the only guard on this path. Send-permission gating (read-only viewer) and the flag-off case are intentionally not host-tested: the former is a pushed flag whose effects are covered by the panel tests below, and with the flag off the panel (and hook target) doesn't exist.
- Panel tests in
app/src/terminal/view/queued_prompts_tests.rs: hint hidden during inline edit and for a locked head (§7, §9); Send-now buttons disabled whencan_send_promptis false (viasend_now_button_disabled_for_test) but not merely because the input is non-empty (§5). Panel tests reuse the host input's own panel when the flag is on — a second panel on the same terminal view would fight over edit-editor focus and commit edits on blur. cargo check+./script/format; manual smoke: queue two prompts during a running conversation, hit Enter twice with an empty input.
Parallelization
Not beneficial: the change is small and tightly coupled (one host file + one panel file share the dispatch helper and the empty-state plumbing). A single agent implements it on this branch (harry/app-4717-change-it-so-hitting-enter-w-an-empty-buffer-and-queued).