Skip to content

chat: ctrl+enter queues a follow-up, and a click takes a queued message back - #1714

Merged
AbirAbbas merged 22 commits into
devfrom
zeropoint95/ctrl-enter-queue-click-unqueue
Oct 1, 2026
Merged

AbirAbbas merged 22 commits into
devfrom
zeropoint95/ctrl-enter-queue-click-unqueue

Conversation

@ZeroPoint95

@ZeroPoint95 ZeroPoint95 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR: ctrl+enter queues a message for after the turn. Each queued message gets its own dim ↳ row above the box, and clicking a row takes it back.

1714

Added

  • Click to unqueue. Click a queued row and that message comes out of the session's queue and back into the box, pasted documents included.
    • The row lights under the pointer, which is the only hint. It lights only where a click would actually work.
    • If the turn already started it, the row stays and the message runs.
  • The take-back works over the wire too. New MethodUnqueueFollowUp (client and server). An older engine just answers "no", so the row stays put.
  • New icon slot GFollowUp (↳). It shares the reply arrow's character, told apart by position, and that argument is recorded in TestOneGlyphOneMeaning.

Changed

  • Queue key: ctrl+q → ctrl+enter. ctrl+q is now unbound on purpose.
    • It only works where the terminal can tell ctrl+enter from enter. Elsewhere it stays a newline.
    • Words only. With a picture (or a picked harness) on the tray it refuses in one line (ctrl+enter queues words alone — …) and changes nothing. The foot doesn't offer it then either.
  • The queue is shown message by message. It used to be one count, after yield · N.
  • The foot while a turn runs: enter steers it in · ctrl+enter queue · esc interrupt.
    • ctrl+enter queue takes the slot ctrl+shift+enter stops and sends used to have.
    • ctrl+shift+enter still works and is still on the ? sheet.
  • Standing orders: ctrl+enter no longer marks a draft as a rule. Use /standing <words>.

Fixed

  • The click did nothing on a normal (hosted) launch. countingAgent (the telemetry tee in cmd/codeaf) handed the surface a copy of each queued stream, so the take-back named a stream the engine never minted and always got false.
    • The tee now maps each copy back to the original stream.
    • New test over the real loopback wire: fails without the fix, passes with it.

Removed

  • The dim line under the queue (queued for after this turn · click takes one back · ctrl+enter queues the draft).
  • ↑ never reaches the queue. It stays the key for the parked block and history.

Manual and docs

  • keys.md, screen.md, hints-and-tips.md, standing-orders.md, the icon design doc and the change entry all say what the code does now.

Checked

  • make pr-ready: laws, manual and internal/tui3 all pass.
  • ⚠️ Six tests are red, and red on clean dev too (checked in a detached worktree at origin/dev). Not caused by this branch:
    • cmd/codeaf: TestDoOnTheRunEngine… ×4 (/private/var vs /var path mismatch)
    • internal/session: TestNewAdmissionReadsSettingsChangedBeforeItsCreation, TestHeldBeltRunStopsWithoutPreparingRepository
  • Driven in tmux against a stub model, through a real engine daemon: queue two, click one, it comes back to the box, the other still runs.

Try it

make build && bin/codeaf

Start a turn, type, press ctrl+enter, then click the ↳ row.

🤖 Generated with Claude Code

ZeroPoint95 and others added 9 commits September 30, 2026 16:52
…ken back

Squash of task/implement-click-to-unqueue-on-co-ee1caf onto dev: queueing
moves from ctrl+q onto ctrl+enter, the queue draws one row per message, a
click takes one back out of the session's queue (session.Agent.UnqueueFollowUp,
across the wire as MethodUnqueueFollowUp), and the standing-order chord is
retired in favour of /standing <words>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t does not name ctrl+enter

The take-back never worked through the normal launch. A hosted chat's agent is
the telemetry tee (countingAgent), whose FollowUp hands the surface a copy of
the stream; the surface named that copy on a click, and the promoted
remote.Agent.UnqueueFollowUp had no receipt for it, so it answered false and
the row stayed. The tee now maps each copy to the stream it copies.

And the ctrl+enter queue clause this branch added to the keys row is removed
at the owner's request: the foot is back to dev's shape, and the chord is named
on the key sheet and on the queued block's dim line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
At the owner's request the keys row under the box keeps to plain enter while a
turn runs: enter steers it in · esc interrupt. The chord still stops and sends
on a terminal that can send it, and the key sheet still lists it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…stood

While a turn runs and there are words in the box, the keys row reads
enter steers it in · ctrl+enter queue · esc interrupt, on a terminal that can
send the chord. The stop-and-send chord keeps working and stays on the key
sheet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… under the queue

The queued block's rows are led by a new vocabulary slot, GFollowUp (↵,
geometry, like the rest of the prompt family), instead of the hollow circle a
waiting task wears. The dim line under the block is gone at the owner's call;
a row now lights under the pointer only where a click would take it back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GFollowUp now draws GReplyIn's byte, at the owner's pick. The two are told
apart by position (a question's room versus the block above the box), and
TestOneGlyphOneMeaning carries that argument.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-queue-click-unqueue

# Conflicts:
#	internal/remote/callclass.go
The lock law (internal/guard's TestEveryLockInTheGuardedTreeUnlocksFromADefer)
wants defer Unlock directly under every Lock; the lookup moves into its own
function so the lock is never held across the round trip to the engine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ZeroPoint95
ZeroPoint95 force-pushed the zeropoint95/ctrl-enter-queue-click-unqueue branch from d8cba35 to 2a3870d Compare October 1, 2026 02:35
…ng the message

FollowUp carries text alone, so queueing over a picture left the picture on
the tray to ride out with the next send while the manual said it was spent.
The key now refuses in one line and changes nothing, as the standing chord it
replaced did, and the foot does not offer it in that state. The manual and
the take-back's comment say what the code does. (Review on #1714.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ZeroPoint95
ZeroPoint95 marked this pull request as ready for review October 1, 2026 02:44
AbirAbbas and others added 3 commits October 1, 2026 12:00
ctrl+enter queues only non-empty words behind this conversation's running
turn; at rest, on the new-chat start page, over a /command, in a room, the
roster or the rewind bar it is plain enter. A decoded chord is honoured
without the kitty reply (modifyOtherKeys); the hint still waits for it.

A take-back marks its row until the session answers, so a turn ending
during the round trip neither starts the removed message nor drops it. The
words merge into this conversation's own composer (appended to a draft,
paste chips renumbered, plain slash tags kept), in click order. Only rows
this window queued light and take a click. Queued rows fit their width.

The manual and change entry say what the code does, including that
/standing keeps pictures and a picked harness on the tray.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…window

The client drops a follow-up's take-back receipt when its stream ends, not
only on a take-back or a dead connection. The engine treats a take-back as
a driver door and honours it only from the connection that queued the
message. The wire format and remote.Version are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ZeroPoint95 and others added 5 commits October 1, 2026 12:24
…-queue-click-unqueue

# Conflicts:
#	internal/tui3/input.go
Brings in #1699 (slash chips anywhere) and #1723. enterLine keeps this
branch's shape (no marked argument) and #1699's plain-tag snapshot:
park(line, false, plain), submitShown/submitImagesShown(..., plain).

A queued follow-up's own demoted ranges join #1699's resting door words on
its transcript line, so a slash word backspaced to plain before ctrl+enter
stays plain when its turn starts. A draft holding a live send-door tag is
a command, so ctrl+enter takes enter's road for it instead of queueing the
words and dropping the tag.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
followRecall named its answer flag `settled`, a field name the live-state
seam law reserves for transcript blocks leaving the live state. It is
not a block and has no edge, so it is renamed rather than exempted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Her merge of origin/dev (654cf26) resolved enterLine the same way this
branch already did; the code is identical, so this side is kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas

Copy link
Copy Markdown
Collaborator

Taking over: @ZeroPoint95, I drove this in the real binary on both roads (plain codeaf and codeaf chat --no-host) and pushed fixes for what broke there. The queue, the ↳ rows and the click take-back all work as described. Here is what changed:

  • dbba0b531 ctrl+enter queues only when there is a running turn to queue behind; otherwise it is plain enter. Three things went wrong before this. On the new-chat start page (ctrl+t) the words were sent into the conversation under the page. Over /cost with nothing running, the literal text /cost went to the model as a message. And a real ctrl+enter from a modifyOtherKeys terminal (xterm, tmux with extended-keys) was dropped without effect. The chord is now honoured wherever the terminal actually sends it. The foot and the tip still wait for the terminal's key-support reply before they mention it.
  • same commit, a take-back never loses words. Clicking a queued row while the box held a draft used to replace the draft, and two quick clicks kept only the last message. Now the message is appended under the draft on a new line, with paste chips renumbered and backspaced-plain slash words kept plain. A row whose take-back is still waiting for the engine's answer is never started, so a turn that ends during the round trip cannot draw the message as sent and then lose it. The words always return to this conversation's composer, even if a task room has the keyboard. Rows this window did not queue (a steer that fell through, or a turn from another window) no longer light up for a click that cannot work. Queued rows wrap to the frame width.
  • ec3b71747 remote receipts. A follow-up's take-back receipt now ends with its stream; before, the client never dropped receipts for follow-ups that simply ran. A take-back is now a driver door, honoured only from the connection that queued the message.
  • b29009509 merge with feat(chat): chip recognised slash-commands anywhere in the message buffer #1699. The queued message's plain ranges now reach its transcript line. A draft with a live /standing or /task tag takes enter's road instead of being queued as bare words.
  • cfd17b563 renames the take-back's settled flag to answered, because that name belongs to the live-state seam law.
  • 771f4e66f, 788b5ef26, 260356d51 are dev syncs. Yours resolved enterLine the same way.

Also checked by hand: a dev-build window queues with ctrl+q against an engine from this branch, and this branch's window on an older engine keeps the row while the message still runs.

Left for later, not done here:

  • Queued rows have no cap, so ~25 queued messages can push the transcript off a small window.
  • Against an older engine (for example --host to a machine on an older codeaf), rows still light under the pointer even though the click does nothing.
  • Clicking a parked (enter-held) row still replaces a draft in the box. That predates this PR.
  • With ctrl+q unbound, a terminal that cannot send ctrl+enter has no queue key at all. The manual says so.

AbirAbbas and others added 4 commits October 1, 2026 13:19
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in #1662 (startup and home off the UI loop) and the v0.6.0 roll-up.
startChatEnter keeps #1662's renewLater shape with this branch's
enterLine(): the start page's ctrl+enter is its enter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

# Conflicts:
#	internal/tui3/chatstart.go
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@AbirAbbas

Copy link
Copy Markdown
Collaborator

Synced with dev (#1662, #1730), now at 63b5fbbfc, CI green. On the new-chat page, ctrl+enter now goes through #1662's background open like enter, and pressing it again while the chat opens does nothing, the same as a second enter. On home, ctrl+enter still asks here, as before. Checked by hand on the rebuilt binary.

@AbirAbbas AbirAbbas left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified by hand on both roads and across old/new engine builds: ctrl+enter queues mid-turn, a click takes a queued message back under the draft, and plain tags stay plain. Merging.

@AbirAbbas
AbirAbbas merged commit 9d00d1c into dev Oct 1, 2026
3 checks passed
@AbirAbbas
AbirAbbas deleted the zeropoint95/ctrl-enter-queue-click-unqueue branch October 1, 2026 19:54
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.

2 participants