Skip to content

feat(composer): type into the terminal (completes #77) - #84

Merged
AltanS merged 3 commits into
mainfrom
feat/type-into-terminal-77
Aug 9, 2026
Merged

feat(composer): type into the terminal (completes #77)#84
AltanS merged 3 commits into
mainfrom
feat/type-into-terminal-77

Conversation

@AltanS

@AltanS AltanS commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Completes @aspiers' #77. Their commit is cherry-picked with authorship preserved; the entry point and armed presentation are reworked on top.

Fixes #74.

What ships

A "Type" toggle in the Controls row, beside Keys. While on, what you type goes to the pane as keystrokes with no trailing Enter, so a TUI that wants bare letters (b, q) can be driven from a phone.

What changed from #77, and why

Entry is a named toggle, not a gesture. The submitted version armed on a bare long press of Send. That reads as saving a tap, but the tap is per session — you arm once and type many keys — and what it actually buys is an accidental hold quietly pointing the phone keyboard at a live terminal. A split Send button was built next and rejected too: a third of the primary action's width, spent every day, for a mode used in bursts. Keys is the real sibling — it exists because a phone keyboard can't send Esc/Tab/arrows/chords, this exists because it can't send bare printable letters — so someone hunting for b finds it where they already looked.

It dies with the pane view. Added disarms on a hidden page and on a composer lock (gone pane, read-only device, idle pause), alongside #77's pane-switch and failed-batch cases. The mirror is polled and the poll stops in all of them, so the next keystroke would land in a terminal nobody is watching. A lost connection is deliberately not a separate trigger: it already surfaces as a failed batch, observed directly rather than inferred from a timer.

Armed state gets a strip in the document flow above the input — the button and textarea are exactly what you stop looking at once typing starts.

Renamed from "Immediate mode", which named the latency rather than the destination.

The pump (ordered, bounded 64-key batches over Herdr's one-connection-per-RPC transport), the Android/IME capture, and the fail-stop are @aspiers' work, untouched.

Also in here

A composer layout pass the fourth toggle forced: an "on" control carries a light-sky state tint (--control-on), the "Controls" tag floats out of the row's flex flow to buy back ~60px of a 390px phone, and attach moves inside the field messenger-style with Send gaining a little air.

Verification

Backend 534 · ctl lifecycle passed · web 104 files / 1716 tests · both typechecks clean. Driven on a real browser at 390px in both themes; geometry and contrast measured rather than eyeballed.

Untested: Android/Gboard IME. jsdom only simulates that event stream — swipe-typing a word and Backspace mid-word are the paths to exercise on a real device.

No version bump; the release commit follows.

aspiers and others added 3 commits August 10, 2026 00:00
Squashed from #77 (five commits; the 0.26.0 release commit is dropped — main cuts
one release when the queued work is done). Adds the machinery: an ordered
one-in-flight pump with bounded 64-key batches, so concurrent send_keys over
Herdr's one-connection-per-RPC transport cannot reorder characters; Android
beforeinput capture for Backspace/Enter and IME-committed strings; and a
fail-stop that discards the queue, disarms, and blurs rather than letting
continued typing become a reply with an Enter attached.

The entry gesture and armed presentation are reworked in the commit that follows;
this one is the mechanism as submitted.
…ane view

Reworks the entry point and armed presentation of the mode added in the commit
before this one. The pump, the Android/IME capture and the fail-stop are untouched.

Entry is a named toggle beside Keys, not a gesture on Send. The submitted version
armed on a bare long press, which reads as a saving of one tap — but the tap is per
session, not per keystroke, and what it actually buys is an accidental hold quietly
pointing the phone keyboard at a live terminal. A split Send button was tried next
and was worse in a different way: a third of the primary action's width, spent every
day on a mode used on a few of them. Keys is the real sibling — it exists because a
phone keyboard can't send Esc/Tab/arrows/chords, this exists because it can't send
bare printable letters — so someone hunting for `b` finds it where they looked.

The mode now also dies on a hidden page and on a composer lock (gone pane, read-only
device, idle pause): the mirror is polled, and in each of those the poll has stopped,
so the next keystroke would land in a terminal nobody is watching. A lost connection
is deliberately not a separate trigger — it already surfaces as a failed batch.

Armed state gets a strip in the document flow above the input, because the button and
textarea are exactly what a user stops looking at once typing starts.

Also lifts the "Controls" tag out of the row's flex flow — a fixed ~60px of a 390px
phone spent on a word that never changes — so four labelled toggles fit unclipped.

Renamed throughout from "Immediate mode", which named the latency rather than the
destination. Raises the ADR bar in .adr/README.md: this rework's lifecycle rule was a
candidate and didn't clear it, so the argument sits in use-direct-typing.ts instead.
…e the field

Three passes over the composer's layout, once the "Type" toggle made the row
four-wide.

An "on" control now carries a light-sky tint (--control-on / -foreground) instead
of the secondary surface. Its own token pair because the two say different things:
secondary is a quieter surface, this is a STATE — an open dock, an armed mode — and
the row has to make that legible at a glance. Authored once so a dock and a mode
can't drift apart, with hover pinned to the same tint (otherwise hovering an on
control repaints it with the ghost hover and reads as switching off). The light half
is tuned by chroma, not luminance: at "very light sky" lightness nothing clears a
useful WCAG ratio against the page, and what makes it visible is being the one
saturated thing in a neutral row. Text on the tint is the number that matters and
clears it — 7.3:1 light, 9.2:1 dark.

Attach moves inside the field, messenger-style. It had a full-height slot to the
left of the input, which spent the widest part of the composer on the least-used
action. Pinned bottom-right rather than centred: the field grows upward as a draft
wraps, and a centred button would drift away from the thumb. `pr-11` keeps a long
line from running under it, and `block` on the textarea stops the wrapper inheriting
the inline baseline gap that left the button hanging past the field's bottom edge.

Send gains a little air (gap-2 → gap-3): with attach gone from the row it is just
the field and Send, and the old spacing left the two looking joined.
@AltanS
AltanS merged commit 4654013 into main Aug 9, 2026
1 check passed
@AltanS
AltanS deleted the feat/type-into-terminal-77 branch August 9, 2026 23:33
tyamanak added a commit to tyamanak/collie that referenced this pull request Aug 10, 2026
Composer Type toggle (AltanS#84) merges cleanly; its two new tests gain the
fork's reflow pref field and setReflow prop (DisplayPrefs grew them in
the mirror-reflow work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vw9GsWBy94wL4wNHP3JQbs
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.

Composer: add a mode for sending key sequences without Enter

2 participants