mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-14 10:39:32 +01:00
fix(pi): leave page keys out, and pin the strip that makes the wheel work
transcriptPageKeys applied to every pi, and a pi in `tuiMode: regular` binds PageUp/PageDown to its editor. It emits no mouse enable, so the flag stays false, and on a fresh pane the buffer is hollow: the wheel sent PageUp, the caret of a multi-line draft moved, and the next keystroke landed on the wrong line. Measured on real pi 1.1.0, 1.4.0 and 1.5.0. Regular mode is not exotic, it only takes one line in pi's settings. Telling the two modes apart would need a new capability, since the paging gate takes no id, so pi has no fallback: a fullscreen pi with "Wheel scrolls local history" on stays where master left it. altScreen, the half of the fix that does the work, was pinned by nothing: reverting it left 1328 tests green. The strip decision table reads its expected value from the same registry entry. pi now has its own block using the bytes it really emits, which also pins that focus reporting survives. The fullscreen default dates from pi 1.0.0, not 1.4.0, so the comments say that, and the sites still calling pi narrow-strip or main-screen by default are corrected. Where a comment listed CLIs it now names the capability. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
58dae1e182
commit
b5aa3fbdf7
14 files changed
+102
-40
No files matched your search
@@ -1180,15 +1180,21 @@ const PI: CliEntry = {
|
||||
// (hasTrustRequiringProjectResources, pi 1.1.0), which would stall a spawned worker on
|
||||
// a dialog. pi reads the user-level `~/.agents/skills` without that prompt, so a pi
|
||||
// user installs it there (`npx skills add Ark0N/Codeman --skill codeman -g`).
|
||||
// pi 1.4.0 measured (tmux pane: alternate_on=1, history_size=0, mouse on, SGR): a fullscreen
|
||||
// TUI that enables tracking once at start. Codeman's replay (tab switch, reload) drops that
|
||||
// mode in xterm, so the wheel hit an empty local buffer and went dead until a redraw. Same
|
||||
// shape as copilot: strip the DECSETs (a drag selects locally), publish `cliMouseTracking`,
|
||||
// and forward the wheel by hand from that flag. pi scrolls on SGR wheel (1 line per report)
|
||||
// and on PageUp/PageDown, which is the fallback for a stale flag.
|
||||
// Fullscreen is pi's default `tuiMode` since 1.0.0 (measured 1.0.0, 1.1.0, 1.4.0, 1.5.0 in
|
||||
// tmux: alternate_on=1, history_size=0, mouse on, SGR): a TUI that enables tracking once at
|
||||
// start. Codeman's replay (tab switch, reload) drops that mode in xterm, so the wheel hit an
|
||||
// empty local buffer and went dead until a redraw. Same shape as copilot: strip the DECSETs
|
||||
// (a drag selects locally), publish `cliMouseTracking`, and forward the wheel by hand from
|
||||
// that flag. pi scrolls on SGR wheel reports, one row each at a low rate, faster on a spin.
|
||||
// `tuiMode: regular` (and every pi below 1.0.0) is inline and emits no mouse DECSET, so the
|
||||
// strip finds nothing and the flag stays false.
|
||||
// ⚠️ No `transcriptPageKeys`, though fullscreen pi does page on PageUp/PageDown. pi binds
|
||||
// those keys to its editor in regular mode too, where they page nothing and move the caret
|
||||
// of a multi-line draft, so a wheel over a fresh regular pane sent the next keystroke to the
|
||||
// wrong line (measured). The cost: a fullscreen pi with "Wheel scrolls local history" on
|
||||
// has no paging fallback.
|
||||
altScreen: 'strip-mux-and-mouse',
|
||||
wheelForward: { mode: 'always' },
|
||||
transcriptPageKeys: true,
|
||||
echo: { policy: 'buffer', anchor: { kind: 'cursor' } },
|
||||
// Measured on a live pi 1.1.0 pane (capture-pane every 250 ms through a turn,
|
||||
// 2026-10-09): pi has no composer glyph. Its composer sits between two `─` rules, and
|
||||
|
||||
@@ -546,7 +546,7 @@ export interface CliCapabilities {
|
||||
* `strip-full` is `isAltScreenStripMode`; `strip-mux-and-mouse` is `isMuxMouseStripMode`;
|
||||
* every other value takes `isMuxAltScreenOnlyStripMode`, so at runtime `preserve` and
|
||||
* `strip-mux-only` are the same row — `preserve` only says what such a CLI's pane
|
||||
* holds (terminal-owned scrollback: a shell, pi), not a different strip.
|
||||
* holds (terminal-owned scrollback: a shell), not a different strip.
|
||||
*
|
||||
* - alt-screen: the tmux CLIENT emits `smcup` as its first bytes at attach, parking
|
||||
* xterm in the scrollback-less alternate buffer; a pane program's own toggles never
|
||||
|
||||
+5
-5
@@ -304,8 +304,8 @@ function cliExportsTruecolor(mode: SessionMode): boolean {
|
||||
* Codex, Claude Code, and Gemini are known, controlled (Ink/React) TUIs that
|
||||
* repaint via cursor positioning, so dropping the alt-screen switch is safe —
|
||||
* content stays in the normal buffer. Excluded: `shell` (arbitrary programs like
|
||||
* vim/less/htop legitimately need the alt screen), `opencode` and `pi` (their own MIDDLE strip,
|
||||
* isMuxMouseStripMode; pi since 1.4.0) and `grok` (a fullscreen alt-screen TUI with
|
||||
* vim/less/htop legitimately need the alt screen), `opencode`, `copilot` and `pi` (their own
|
||||
* MIDDLE strip, isMuxMouseStripMode) and `grok` (a fullscreen alt-screen TUI with
|
||||
* mouse support). Keep parity with the replay-side strip (`stripReplayBuffer` in
|
||||
* session-routes.ts); the table in `CliCapabilities.altScreen` is pinned for every
|
||||
* stock CLI in test/claude-scrollback-strip.test.ts.
|
||||
@@ -315,8 +315,8 @@ function cliExportsTruecolor(mode: SessionMode): boolean {
|
||||
* strip the alt-screen toggles too whenever the session is tmux-backed, and
|
||||
* pi/opencode ALWAYS are (both refuse the direct-PTY fallback). What exclusion
|
||||
* actually buys is the rest of the full strip: `\x1b[3J` survives, and for the
|
||||
* narrow strip the mouse-tracking DECSETs too. pi (1.4.0, measured) is a
|
||||
* mouse-aware fullscreen TUI like opencode and copilot, so it takes the mouse
|
||||
* narrow strip the mouse-tracking DECSETs too. pi (fullscreen by default since
|
||||
* 1.0.0) is a mouse-aware TUI like opencode and copilot, so it takes the mouse
|
||||
* strip (isMuxMouseStripMode): a drag selects locally and `cliMouseTracking`
|
||||
* keeps the wheel working across a buffer replay.
|
||||
*/
|
||||
@@ -3001,7 +3001,7 @@ export class Session extends EventEmitter {
|
||||
// redraws overwrite only the cells they target, so non-erased rows keep
|
||||
// their content. Gated to Codex/Claude/Gemini (isAltScreenStripMode).
|
||||
//
|
||||
// Every OTHER mode (shell/antigravity/pi/grok/deepseek/omp) gets the NARROW strip
|
||||
// Every OTHER mode (shell/antigravity/grok/deepseek/omp) gets the NARROW strip
|
||||
// when it is tmux-backed: alt-screen toggles only, because the sequence that breaks
|
||||
// scrollback there is tmux's own client-side smcup at attach, not anything the
|
||||
// program in the pane emitted (issue #205, see isMuxAltScreenOnlyStripMode).
|
||||
|
||||
@@ -956,8 +956,8 @@ export interface SessionState {
|
||||
/**
|
||||
* True while the CLI in the pane has a mouse-tracking DECSET on, as observed
|
||||
* by the server on its way out of the stream (those sequences are stripped for
|
||||
* the strip-full and strip-mux-and-mouse modes, claude/codex/gemini and opencode
|
||||
* under tmux, so the browser can never see them itself). The browser
|
||||
* the strip-full and strip-mux-and-mouse modes under tmux, so the browser can
|
||||
* never see them itself). The browser
|
||||
* hand-encodes a click report ONLY when this is true; without it, every click
|
||||
* sent mouse reports to a CLI that never asked for them.
|
||||
*/
|
||||
|
||||
@@ -1149,8 +1149,8 @@
|
||||
|
||||
// A plain left-click reported to the CLI, the primary pane's desktop click
|
||||
// (terminal-ui.js _handleDesktopTerminalClick) aimed at this pane. The
|
||||
// server strips the mouse DECSETs of some modes (opencode's since #555, so a
|
||||
// drag selects text), which leaves this xterm's own mouse encoder idle for
|
||||
// server strips the mouse DECSETs of some modes (so a drag selects text),
|
||||
// which leaves this xterm's own mouse encoder idle for
|
||||
// them; without this a click in such a pane never reached the CLI. Only
|
||||
// while this pane's session has tracking on (cliMouseTracking), through the
|
||||
// same skips as the primary pane. Bubble phase, as there. The target is
|
||||
|
||||
@@ -1572,8 +1572,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
// A real mouse click normally reaches the PTY through xterm's own mouse
|
||||
// encoder, but that encoder only runs while mouseTrackingMode is ON — and
|
||||
// the server strips the enabling DECSETs from claude/codex/gemini output
|
||||
// (isAltScreenStripMode, session.ts) and from opencode's (isMuxMouseStripMode,
|
||||
// so a drag selects text) so the wheel keeps scrolling
|
||||
// (isAltScreenStripMode, session.ts) and from the mouse-strip modes'
|
||||
// (isMuxMouseStripMode, so a drag selects text) so the wheel keeps scrolling
|
||||
// scrollback. Desktop clicks therefore stopped reporting entirely (the
|
||||
// same breakage the mobile touchend tap branch above works around).
|
||||
// Hand-encode the SGR report for plain left-clicks on those sessions.
|
||||
@@ -5942,8 +5942,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
* literal text (`[<0;88;20M`) and they garble the next line typed.
|
||||
*
|
||||
* Fails toward silence: an unknown or stale flag reports nothing rather than
|
||||
* injecting bytes. After a server restart the flag is false until the CLI
|
||||
* re-emits its DECSET, which closing and reopening a dialog does.
|
||||
* injecting bytes. After a server restart the flag is false until the DECSET
|
||||
* passes again, which tmux does itself when the server re-attaches to the pane.
|
||||
*
|
||||
* `sessionId` defaults to the primary pane's session; a TerminalTile passes
|
||||
* its own (through _handleDesktopTerminalClick's target), never the active one.
|
||||
@@ -6065,7 +6065,7 @@ Object.assign(CodemanApp.prototype, {
|
||||
if (mode && mode !== 'none') return false;
|
||||
const session = this.sessions?.get(target.sessionId || this.activeSessionId);
|
||||
const sessionMode = session?.mode || 'claude';
|
||||
// GitHub Copilot CLI (and pi, same shape since 1.4.0) scrolls its own transcript on SGR wheel reports (checked with xterm
|
||||
// GitHub Copilot CLI (and pi, fullscreen by default) scrolls its own transcript on SGR wheel reports (checked with xterm
|
||||
// encoding them natively). Its mouse DECSETs are stripped server-side so a drag selects
|
||||
// locally (strip-mux-and-mouse), which also means xterm no longer encodes the wheel: send the
|
||||
// same reports by hand, but only while the CLI is actually tracking the mouse. Not gated on a
|
||||
@@ -6408,8 +6408,8 @@ Object.assign(CodemanApp.prototype, {
|
||||
* The reason is that the habit and xterm's Shift mean different things once
|
||||
* the DECSETs are stripped. xterm reads Shift as "force selection" ONLY while
|
||||
* the app actually has mouse tracking on; the server strips those DECSETs for
|
||||
* claude/codex/gemini (isAltScreenStripMode) and opencode (isMuxMouseStripMode),
|
||||
* so xterm's mouseTrackingMode is
|
||||
* claude/codex/gemini (isAltScreenStripMode) and the mouse-strip modes
|
||||
* (isMuxMouseStripMode), so xterm's mouseTrackingMode is
|
||||
* permanently `none`, that branch is unreachable, and Shift instead falls into
|
||||
* `_onIncrementalClick` — EXTEND an existing selection. Extending is a no-op
|
||||
* when `selectionStart` is null, so the drag never anchors and no selection is
|
||||
|
||||
@@ -332,7 +332,7 @@ const MOUSE_TRACKING_PATTERN = /\x1b\[\?(?:1000|1001|1002|1003|1005|1006|1007)[h
|
||||
* - `strip-full` (claude/codex/gemini): alt-screen toggles, `3J` and mouse DECSETs.
|
||||
* xterm obeys the toggles by switching to its scrollback-less alt buffer and wiping
|
||||
* saved lines, so history disappeared on tab switch.
|
||||
* - `strip-mux-and-mouse` (opencode, tmux-backed): alt-screen toggles and mouse DECSETs.
|
||||
* - `strip-mux-and-mouse` (tmux-backed): alt-screen toggles and mouse DECSETs.
|
||||
* A replayed tracking enable parks xterm in report mode, where a drag goes to the CLI
|
||||
* instead of selecting text (and Ctrl+C without a selection is opencode's app_exit).
|
||||
* `3J` stays: a TUI is not a `clear` consumer.
|
||||
|
||||
Reference in new issue
Block a user