Repository navigation
feat(terminal): an input line on phones, instead of typing into the grid - #76
Merged
Merged
Conversation
One-tap messages (continue, yes, no, commit and push, retry to start with), as collie's Quick dock has. A reply goes through the composer's own send path, so it is queued while the agent works and taken as the answer when a question card is open; a draft in the box is left alone. The list lives in each device's settings: quick replies are a phone thing (a PC has a keyboard), so nothing is stored on the server. Settings -> Quick replies edits it (up to 12, 200 characters each; blank rows are kept while being typed and never shown), and a button beside the paperclip hides or shows the row, remembered per device. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The row takes a line above the message box on every pane; it now waits for the ⚡ button, and the choice is remembered per device as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Transcripts already record what each request filled:
- Codex: token_count events with last_token_usage and model_context_window
- Claude Code: assistant usage, input + cache creation + cache read,
subagent (sidechain) entries skipped
- omp/omo/gjc: message usage, input + cacheRead + cacheWrite
ConversationMetadata gains an optional `context { used, window }`. The
window is only what the transcript leaves no doubt about: Codex states it,
and a Claude request past 200k can only have run in the 1M window (kept
through a compaction). Otherwise the line shows the tokens used, never a
guessed percentage.
The composer shows `74% left` or `68k used`, red at 20% or less, with the
counts on hover. PaneTerminal's metadata dedupe compared only model and
effort, so it now compares the context too, or the number would never move.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, a loading state From comparing the chat with chatmux's: - A failed tool call looked like a success inside "Worked for…". Tool parts gain `error`: Claude's tool_result is_error and omp's toolResult isError, and for Codex, which records no flag, the output's own verdict (exit code, "Script failed", "apply_patch verification failed"; a completed script is judged as a whole). The row reads "failed" in red, its output is headed Error, and the block header counts failures. - A file path in a tool row opens the FileViewer, like paths in prose. - Codex's apply_patch, bare or as the string an exec script passes to tools.apply_patch, renders as a per-file diff and its row is summed up by the files it touches (shared/patch.ts, used by server and client). - The jump-to-latest button showed only for new messages; it now shows whenever the reader is scrolled up (a round arrow), and stays the "New messages" pill when there are some. - The chat said "No conversation yet" until the first answer came back; it now says it is loading. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
More from comparing with chatmux's chat: - Images the user sent: Claude transcripts keep pasted images as base64 in the user entry. The parser now emits an image part naming it (`<entry uuid>:<block index>`, png/jpeg/gif/webp only) instead of the data, and GET /api/pane/conversation/image decodes it on request (immutable cache), so a 2 s poll never carries megabytes. Images this app attaches appear as `@…png` mentions and show through /api/fs/file. Thumbnails sit above the user bubble; a tap opens them. - Edit/MultiEdit render as one line diff (src/lib/diff.ts, an LCS that falls back to removed-then-added past 250k line pairs). - A compaction's summary, which was dropped, is a divider that opens to it. - Markdown headings #### to ###### render as h4-h6. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… menu - Tool outputs are cut at 4000 characters in the page. A cut one now keeps its call id and full length (output_ref, output_size; the cut moved into server/tool-output.ts, shared by the Claude, omp and Codex parsers). GET /api/pane/conversation/tool-output finds the call's result in the transcript and returns it as text (up to 2 MB, immutable cache); the row offers "Show the whole output (48k characters)" and shows it in a box that scrolls on its own. - The `/` menu for Claude adds user and project skills (SKILL.md) and the skills and commands of plugins enabled in ~/.claude/settings.json, as /<plugin>:<name>. For Codex it adds saved prompts (/prompts:<name>) and its skills, which Codex takes as `$name`: a `$` where a word starts opens them, only in a Codex pane. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a phone, text typed in the terminal lens came out repeated ("널에
추가로 폰에서는채팅을 모바일 가터미널에 미널에…"): the keyboard rewrites
what it typed (dictation revising a phrase, an IME composing a Korean
syllable, autocorrect), and xterm sends each revision as new keys a
terminal cannot take back. Termius documents the same problem; Moshi and
Termius both answer it with a panel where the text is finished first and
sent whole.
- On a coarse pointer, the terminal lens gets TerminalInput above the key
bar: a line that grows to four, Enter or the button sends it, an empty
line's button presses Enter alone. It is written with the keyboard's own
editing and sent whole.
- It goes as a submit with `typed`: the server types it like the keyboard
(send_text, then Enter after the usual gap) instead of agent.prompt,
which refuses while an agent waits, since in the terminal an answer to
an open menu is what the user means. Several lines go as one paste.
- The grid's hidden textarea gets inputmode=none, so a tap no longer raises
the keyboard, and key-bar taps leave focus in the line. A ⌨ key on the
key bar switches to typing straight into the grid, remembered per device.
- Desktops (fine pointer) are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #74 (← #73 ← #72 ← #71 ← #70). Merge in order. It merges cleanly with #75.
Bug
On a phone, text typed in the terminal lens came out repeated. The user's own report showed it: "널에 추가로 폰에서는채팅을 모바일 가터미널에 미널에…".
A phone keyboard rewrites what it typed: dictation revises a phrase, an IME composes a Korean syllable, autocorrect replaces a word. xterm sends each revision as new keys, and a terminal cannot take back keys it has already received.
What
pointer: coarse).TerminalInputsits above the key bar.submitwith the newtyped: true. The server skipsagent.prompt, which refuses while an agent waits, and types the payload withsend_text, then Enter after the usualSUBMIT_DELAY_MSgap. In the terminal, an answer typed into an open menu is what the user means.typedand behave as before.inputmode="none", and key-bar taps leave focus in the line.herdr-web-ui:direct-typing.Tests
bun run typecheck;bun test426/426.submit.contract.test.ts): atypedsubmit to an agent reported blocked types2then\r, with the Enter's gap kept. The existing test that an untyped submit is refused still passes.test:ui10/10, with a new touch scenario:inputmode=nonetypedsubmit to the right pane, and the line clears on confirmation\recho 안녕하세요 모바일 입력arrived whole and ran.Not verified
A real phone keyboard, i.e. Gboard/iOS dictation and the Korean IME. The headless touch emulation proves the paths, not the keyboards.
🤖 Generated with Claude Code