Repository navigation
fix: composer geometry and terminal-pane control (4 reported defects) - #66
Merged
Merged
Conversation
…resh and a send line
Four defects reported against v7.6.0/7.7.0 that were never actually in a commit.
1. The interrupt pill sat UPSTREAM of the textarea in the composer's flex row, so
starting a turn shoved the placeholder ~70px right and re-wrapped the hint. It
now lives downstream of the textarea; the fade-in slides from the other side.
2. The textarea's height was recomputed only from restoreComposerState(), so boot,
setLang(), a panel toggle, a window resize and the pill entering the row all left
the wrapped second line clipped. One autosizeInput() helper now owns it and is
called from every path that can change the wrap.
3. A terminal whose socket died silently kept focus in xterm's hidden textarea and
swallowed every key until you switched tabs and back (showTerminalView() ends on
term.focus(), which is why that worked). Reconnect and a click on the pane now
refocus it, and a new header button reconnects unconditionally.
4. .center.term-mode hides the composer, so a terminal tab had no way to send a line
except typing into the pane -- exactly what a stale socket takes away. A send line
under the pane writes the same {type:'input'} frame term.onData() writes, so it
works on an agent tab and on the engine pane, where it answers a blocking prompt.
…t for composer-terminal.test.js reads public/index.html and asserts the four invariants behind the fixes in 417d31a: the pill's position in the flex row, that inEl.scrollHeight is read in exactly ONE place, the refresh button's statement order (clear exited -> detach entry.ws -> close -> reconnect), and the send line's frame shape (CR, one frame, refuses a non-OPEN socket). It caught two things while being written: a sixth open-coded height computation in the slash-command inserter that the original patch missed, and the fact that the composer really is hidden by a selector LIST, not a rule of its own.
…on, ignore a stale terminal close An adversarial review of the diff (independent non-Claude advisor) found four ways the result still fell short of the task: - toggle() measured the composer at the START of the 250ms panel animation, i.e. the width the user was leaving. A ResizeObserver on the textarea covers the whole animation and a split-pane drag with it; it compares clientWidth first, because the callback sets the height and would otherwise re-enter forever. - The staleness test in the terminal's onclose guarded only the reconnect, not the "disconnected" paint above it. refreshTerminalSession() detaches before it closes, so the old socket's close event lands after the new one is ready and marked a working pane dead. The guard is now the first line of the handler. - The send line refused an empty string, which made a bare Return -- the one key you most need at a [Y/n] prompt -- the one key it could not send. - That line is shared by every pane, so a draft typed for one terminal was sitting in it when another was opened. showTerminalView() clears it. test/composer-terminal.test.js grows five assertions (46 total); each was mutation- checked by reverting the fix and confirming the failure.
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.
Four defects reported against the shipped build. None was a regression —
git log -Son every identifier is empty, so each was work that never reached a commit.
1 & 2 — the composer
#interruptPillsat upstream of<textarea id="input">in the.iwflex row.Every earlier sibling owns the text's left edge, so starting a turn shoved the
placeholder ~70px right and narrowed the box enough to re-wrap the hint. One report,
two symptoms. The pill now sits downstream; the fade-in slides from the other side.
restoreComposerState).Boot (markup ships
rows="1"),setLang()(a longer translation wraps differently),a sidebar toggle, a window resize, fonts landing after first paint and the pill
entering the row all change the WRAP without changing the VALUE.
autosizeInput()now owns the cap (
COMPOSER_MAX_H) and is called from all seven paths. There weresix open-coded copies of the formula, not five — the slash-command inserter was
the one the first pass missed, and the test caught it.
3 & 4 — the terminal pane
reconnect swaps
entry.wsbut leaves focus wherever it drifted, so the pane lookslive and swallows every key. Switching tabs away and back worked only because
showTerminalView()ends onterm.focus().ws.onopenand amouseupon the hostnow do the same — the latter skipped while a selection is being dragged out. The
v7.6.0 server heartbeat is the other half and only the other half: it reaps a dead
TCP path within ~60s, which is a timeout, not an answer to "it is stuck now".
#termRefreshBtn+refreshTerminalSession()reconnects unconditionally. Orderis the fix: clear the latched
exited→ nullentry.ws→ close the old socket →reconnect.
onclosecomparesentry.ws === ws, so detaching first is what stops thedying socket racing a second reconnect against the new one.
.term-send—.center.term-mode > .iaisdisplay: none, so a terminal tab hadno input surface but the pane itself, which is exactly what a stale socket takes away.
It writes the same
{type:'input'}frameterm.onData()writes, so it works on anagent tab and on the subscription engine's pane, where it is how a blocking permission
prompt gets answered.
\rnot\n, text and CR in ONE frame.Three new i18n keys in all five locales.
Tests
test/composer-terminal.test.js— 41 structure assertions onpublic/index.html,wired into the
npm testchain (66 files now).Full suite on this branch:
EXIT=0, 1993ok, 0 failed.Not covered: the live browser/WebSocket path — needs a manual click-through.