Files
galaxy/specs/APP-4717/TECH.md
T

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

Proposed changes

  1. Shared dispatch helper on Input (app/src/terminal/input.rs): extract the body of the QueuedPromptsPanelEvent::SendNow arm into fn 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.
  2. Panel send state (app/src/terminal/view/queued_prompts_panel.rs):
    • can_send_prompt: bool (host-pushed via the change-detecting set_can_send_prompt setter) — whether the terminal can send prompts at all (false for read-only shared-session viewers, via the existing SharedSessionStatus::is_reader() helper). Gates the Send-now buttons (re-runs update_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 reads is_empty live at decision time, so the Enter decision cannot trail same-update buffer changes. A subscription to the host editor's Edited/BufferReplaced events re-renders the panel on empty↔non-empty transitions (a cached host_editor_was_empty flag only damps these notifications), mirroring the CLIAgentSessionsModel pattern below. The panel observes the CLI-agent rich input itself (a CLIAgentSessionsModel subscription plus a live is_input_open read — Enter submits to the CLI agent while it is open) and exposes enter_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.
  3. Enter path: inlined in input_enter's dispatch chain (before maybe_launch_cloud_handoff_request): when panel.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 via send_queued_row_immediately; a locked head means Enter does nothing.
  4. Push sites: Input seeds can_send_prompt at panel construction (is_reader) and passes the editor handle in; TerminalView::on_self_role_updated pushes set_can_send_prompt(role.can_execute()) when the shared-session role changes (panel reached via the Input::queued_prompts_panel() accessor).
  5. Header hint rendering: render_header appends 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's sub_text_color; the keycap glyph uses internal_colors::text_disabled so it is dimmer. Spacing follows the message-bar hint rules (render_message_bar_items): 8px label→keycap, 4px keycap→text.
  6. Telemetry (app/src/server/telemetry/events.rs): new event QueuedPromptSentNow { origin: TelemetryQueuedQueryOrigin, trigger: QueuedPromptSendNowTrigger } with QueuedPromptSendNowTrigger { SendNowButton, EnterOnEmptyInput }, payload + descriptions following the adjacent QueuedPrompt* 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.rs next to the existing queued-panel host tests (L1277+), driving input_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_locked filter 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 when can_send_prompt is false (via send_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).