mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
docs(split): comments say what TerminalTile does now
The rename from SplitTerminalPane carried comments over that the PR 1
changes made untrue:
- constants.js buildSplitPickerSessions said TerminalTile._sendResize has
no detached check (it stands aside like the primary pane) and that Pane
B sends no `seq` (its input rides the exactly-once queue). The
conclusions stay: a detached session's window owns its PTY size, and a
session with no PTY has a pane nothing feeds or reads.
- terminal-split.js said TerminalTile has no "dims unchanged" skip (it has
_lastSentDims), and its @loadorder still ended at respawn-ui.js rather
than tile-grid.js.
- terminal-tile.js still called every pane "Pane B", said a shell load
lands in "a 50000-line xterm" (the scrollback is an option now) and
that a TUI session always gets a full replay (with boundedLoad, grid
tiles get the bounded window), and told some reasons as history ("an earlier draft", "used
to", "It LOOKED intermittent"). Those now give the reason in the
present tense, and the comments touched lose their em dashes.
writeChunked's doc comment is left as it is: the perf work rewrites
that function and owns its comment.
Comments only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1612,21 +1612,18 @@ function buildSplitPickerSessions(sessions, sessionOrder, excludeId, detachedIds
|
|||||||
const result = [];
|
const result = [];
|
||||||
for (const id of sessionOrder) {
|
for (const id of sessionOrder) {
|
||||||
if (id === excludeId) continue;
|
if (id === excludeId) continue;
|
||||||
// A detached (popped-out) session's own window already yields its PTY
|
// A detached (popped-out) session's own window owns its PTY size (see
|
||||||
// size (see sendResize's detachedElsewhere guard in terminal-ui.js) —
|
// sendResize's detachedElsewhere guard in terminal-ui.js;
|
||||||
// Pane B's TerminalTile._sendResize() has no such check, so letting
|
// TerminalTile._sendResize() stands aside the same way), so Pane B could
|
||||||
// one into the picker put its detached window and Pane B in a fight over
|
// only show it at a size it cannot set.
|
||||||
// the same PTY's dimensions.
|
|
||||||
if (detachedIds?.has?.(id)) continue;
|
if (detachedIds?.has?.(id)) continue;
|
||||||
const session = sessions.get(id);
|
const session = sessions.get(id);
|
||||||
if (!session) continue;
|
if (!session) continue;
|
||||||
// A session with no PTY attached (exited CLI, a crash-looped session
|
// A session with no PTY attached (exited CLI, a crash-looped session
|
||||||
// whose breaker tripped, a restore that failed to re-attach) has nothing
|
// whose breaker tripped, a restore that failed to re-attach) has nothing
|
||||||
// reading its tmux pane. TerminalTile never does selectSession()'s
|
// reading its tmux pane, and the split never does selectSession()'s
|
||||||
// re-attach POST, so its socket would open onto a pane nothing feeds:
|
// re-attach POST: Pane B would open a healthy-looking socket onto a pane
|
||||||
// no terminal events, and Session.write() silently drops every keystroke
|
// that nothing feeds and nothing reads.
|
||||||
// with no ack either way (Pane B sends no `seq`), so the loss is
|
|
||||||
// invisible — the healthy socket never trips the disconnect banner.
|
|
||||||
if (session.pid === null) continue;
|
if (session.pid === null) continue;
|
||||||
result.push({ id, label: session.name || 'Session' });
|
result.push({ id, label: session.name || 'Session' });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
* @dependency terminal-tile.js (window.TerminalTile)
|
* @dependency terminal-tile.js (window.TerminalTile)
|
||||||
* @dependency constants.js (window.CodemanSplitPane, SPLIT_PANE_MIN_WIDTH)
|
* @dependency constants.js (window.CodemanSplitPane, SPLIT_PANE_MIN_WIDTH)
|
||||||
* @loadorder 7.5 of 16, loaded after terminal-tile.js and before respawn-ui.js
|
* @loadorder 7.5 of 16, loaded after terminal-tile.js and before tile-grid.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Object.assign(CodemanApp.prototype, {
|
Object.assign(CodemanApp.prototype, {
|
||||||
@@ -281,10 +281,9 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
// frame). Coalesced to one call per animation frame below — a raw
|
// frame). Coalesced to one call per animation frame below — a raw
|
||||||
// mousemove stream fires far faster than the browser repaints, and
|
// mousemove stream fires far faster than the browser repaints, and
|
||||||
// without the rAF gate each event did a full xterm reflow on BOTH
|
// without the rAF gate each event did a full xterm reflow on BOTH
|
||||||
// panes AND sent Pane B a `{t:'z'}` resize frame (TerminalTile has
|
// panes AND sent Pane B a `{t:'z'}` resize frame, which fanned out
|
||||||
// no client-side "dims unchanged" skip), which fanned out into a
|
// into a `tmux resize-window` child plus a SIGWINCH per frame, roughly
|
||||||
// `tmux resize-window` child plus a SIGWINCH per frame — roughly fifty
|
// fifty of each dragging across half a wide viewport.
|
||||||
// of each dragging across half a wide viewport.
|
|
||||||
const applyDragPercent = (clientX) => {
|
const applyDragPercent = (clientX) => {
|
||||||
const container = divider.parentElement;
|
const container = divider.parentElement;
|
||||||
// The split can auto-collapse mid-drag (the other pane's session
|
// The split can auto-collapse mid-drag (the other pane's session
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
(function (global) {
|
(function (global) {
|
||||||
// How long a scroll-to-top history pull may hold Pane B's live output.
|
// How long a scroll-to-top history pull may hold this pane's live output.
|
||||||
const HISTORY_PULL_TIMEOUT_MS = 10000;
|
const HISTORY_PULL_TIMEOUT_MS = 10000;
|
||||||
|
|
||||||
// How much of a replay is queued in xterm at once: a 1 MiB load goes in one
|
// How much of a replay is queued in xterm at once: a 1 MiB load goes in one
|
||||||
@@ -85,10 +85,10 @@
|
|||||||
this.mountEl = mountEl;
|
this.mountEl = mountEl;
|
||||||
this.sessionMode = opts.mode;
|
this.sessionMode = opts.mode;
|
||||||
this.fontSettings = opts.fontSettings || {};
|
this.fontSettings = opts.fontSettings || {};
|
||||||
// Live reference (not a snapshot) to the app's detachedSessions Set —
|
// Live reference (not a snapshot) to the app's detachedSessions Set:
|
||||||
// detaching this session AFTER the split is already open must still be
|
// detaching this session AFTER the pane opened must still be seen by
|
||||||
// seen by _sendResize() below, or it re-creates the exact PTY-size
|
// _sendResize() below, or this pane and the session's own window fight
|
||||||
// fight the split picker already refuses to open at pick time.
|
// over the PTY's size (which the split picker refuses at pick time).
|
||||||
this.detachedSessions = opts.detachedSessions;
|
this.detachedSessions = opts.detachedSessions;
|
||||||
// Lines of scrollback this pane's xterm keeps (the grid passes its smaller
|
// Lines of scrollback this pane's xterm keeps (the grid passes its smaller
|
||||||
// TILE_SCROLLBACK) and its font size (the grid's own tile font); absent,
|
// TILE_SCROLLBACK) and its font size (the grid's own tile font); absent,
|
||||||
@@ -200,17 +200,18 @@
|
|||||||
|
|
||||||
this.terminal.onData((data) => this._onTerminalData(data));
|
this.terminal.onData((data) => this._onTerminalData(data));
|
||||||
|
|
||||||
// Pane B has no gates of its own by default, so every app-level chord
|
// xterm has no gates of its own, so every app-level chord that the
|
||||||
// that the document capture-phase handler (app.js) only preventDefault()s
|
// document capture-phase handler (app.js) only preventDefault()s (never
|
||||||
// — never stopPropagation()s — reaches xterm here too and writes its raw
|
// stopPropagation()s) would reach this xterm too and write its raw byte
|
||||||
// byte/escape sequence into THIS session's PTY on top of whatever the app
|
// or escape sequence into THIS session's PTY on top of whatever the app
|
||||||
// action already did to Pane A (COD-153; mirrors the primary pane's own
|
// action did (COD-153). These are the primary pane's gates
|
||||||
// gates at terminal-ui.js's attachCustomKeyEventHandler: command palette,
|
// (terminal-ui.js attachCustomKeyEventHandler): command palette,
|
||||||
// Alt+1-9/[/] tab nav, Alt+B sidebar toggle, Ctrl+Z suspend, Shift/Ctrl+Enter
|
// Alt+1-9/[/] tab nav, Alt+B sidebar toggle, the tile grid's chords,
|
||||||
// newline, and smart-copy Ctrl+C/Ctrl+Shift+C). Routed through the same
|
// Ctrl+Z suspend, Shift/Ctrl+Enter newline, and smart-copy
|
||||||
// registry-aware predicates so a rebind or a disable restores plain
|
// Ctrl+C/Ctrl+Shift+C. Routed through the same registry-aware
|
||||||
// terminal behavior here too. Ctrl+V goes through the primary pane's
|
// predicates so a rebind or a disable restores plain terminal behavior
|
||||||
// paste trap (image-input.js), aimed at this pane (below).
|
// here too. Ctrl+V goes through the primary pane's paste trap
|
||||||
|
// (image-input.js), aimed at this pane (below).
|
||||||
this.terminal.attachCustomKeyEventHandler((ev) => {
|
this.terminal.attachCustomKeyEventHandler((ev) => {
|
||||||
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return true;
|
if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return true;
|
||||||
if (
|
if (
|
||||||
@@ -240,13 +241,11 @@
|
|||||||
global.app?._handleImagePaste?.({ terminal: this.terminal, sessionId: this.sessionId });
|
global.app?._handleImagePaste?.({ terminal: this.terminal, sessionId: this.sessionId });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Ctrl+Z (SIGTSTP/job-control suspend): mirrors terminal-ui.js's own
|
// Ctrl+Z (SIGTSTP/job-control suspend), as terminal-ui.js swallows it:
|
||||||
// swallow — in a plain shell session this is the user's own
|
// in a plain shell session this is the user's own job-control tool and
|
||||||
// job-control tool and must reach the PTY, but in every other mode
|
// must reach the PTY, but in every other mode (claude/omp/pi/codex/...)
|
||||||
// (claude/omp/pi/codex/...) it silently stops an unattended agent
|
// it silently stops an unattended agent loop dead. This pane has its
|
||||||
// loop dead. Pane B has its own PTY/session and must not send a
|
// own session and applies the same rule to it.
|
||||||
// suspend into a non-shell one just because the primary pane's own
|
|
||||||
// gate lives elsewhere.
|
|
||||||
if (
|
if (
|
||||||
ev.type === 'keydown' &&
|
ev.type === 'keydown' &&
|
||||||
ev.key.toLowerCase() === 'z' &&
|
ev.key.toLowerCase() === 'z' &&
|
||||||
@@ -258,15 +257,14 @@
|
|||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Shift+Enter / Ctrl+Enter: insert a newline instead of submitting.
|
// Shift+Enter / Ctrl+Enter: insert a newline instead of submitting, as
|
||||||
// Mirrors terminal-ui.js's own handling — xterm sends plain \r for
|
// terminal-ui.js does. xterm sends plain \r for every Enter variant,
|
||||||
// every Enter variant, so an Ink app (Claude Code) can't tell a
|
// so an Ink app (Claude Code) can't tell a newline from a submit, and
|
||||||
// newline from a submit. Without this gate, Pane B's onData would
|
// without this gate this pane's onData would send that bare \r and
|
||||||
// send that bare \r straight over the WS and submit an incomplete
|
// submit an incomplete prompt instead of adding a line to it. Targets
|
||||||
// prompt instead of adding a line to it. Targets THIS pane's own
|
// THIS pane's own session (this.sessionId), never the primary pane's
|
||||||
// session (this.sessionId), never the primary pane's
|
|
||||||
// activeSessionId, and has no local-echo overlay of its own to flush
|
// activeSessionId, and has no local-echo overlay of its own to flush
|
||||||
// first (Pane B is deliberately plainer — see the fileoverview).
|
// first (this pane is deliberately plainer, see the fileoverview).
|
||||||
// Swallow keypress/keyup too (xterm would send \r for a Shift-only keypress); only keydown sends.
|
// Swallow keypress/keyup too (xterm would send \r for a Shift-only keypress); only keydown sends.
|
||||||
if (ev.key === 'Enter' && (ev.shiftKey || ev.ctrlKey)) {
|
if (ev.key === 'Enter' && (ev.shiftKey || ev.ctrlKey)) {
|
||||||
if (ev.type === 'keydown') {
|
if (ev.type === 'keydown') {
|
||||||
@@ -316,17 +314,13 @@
|
|||||||
|
|
||||||
// Load existing scrollback before going live. The WS below is
|
// Load existing scrollback before going live. The WS below is
|
||||||
// subscribe-only (ws-routes.ts sends nothing on connect, only future
|
// subscribe-only (ws-routes.ts sends nothing on connect, only future
|
||||||
// 'terminal' events), so without this Pane B stays blank until the
|
// 'terminal' events), so without this the pane stays blank until the
|
||||||
// target session happens to produce new output. It LOOKED
|
// session happens to produce new output. The resize _sendResize() sends
|
||||||
// intermittent rather than always-broken because _sendResize() below
|
// on open is no substitute: tmux repaints on a resize, but
|
||||||
// often nudges the shared session's real tmux window to a new size,
|
// Session.resize() (session.ts) skips one that matches the session's
|
||||||
// and tmux repaints its current screen on resize — that repaint was
|
// last size, and then nothing repaints at all. The await covers the
|
||||||
// getting captured and streamed here, incidentally populating the
|
// whole chunked replay, not just the fetch, so a live frame from the
|
||||||
// pane. When Pane B's computed dimensions happened to already match
|
// socket below can never land in the middle of it.
|
||||||
// the session's last-known size, Session.resize() (session.ts) skips
|
|
||||||
// the resize as a no-op, no repaint fires, and the pane stayed blank.
|
|
||||||
// The await covers the whole chunked replay, not just the fetch, so a
|
|
||||||
// live frame from the socket below can never land in the middle of it.
|
|
||||||
await this._loadBuffer();
|
await this._loadBuffer();
|
||||||
if (this._destroyed) return;
|
if (this._destroyed) return;
|
||||||
|
|
||||||
@@ -372,8 +366,8 @@
|
|||||||
} else if (msg.t === 'r') {
|
} else if (msg.t === 'r') {
|
||||||
// Server-triggered refresh (SSE backpressure cleared, terminal
|
// Server-triggered refresh (SSE backpressure cleared, terminal
|
||||||
// data was dropped). The primary pane routes this to
|
// data was dropped). The primary pane routes this to
|
||||||
// _onSessionNeedsRefresh (app.js) — Pane B has its own
|
// _onSessionNeedsRefresh (app.js); this pane has its own buffer
|
||||||
// buffer loader for the same reason connect() does.
|
// loader for the same reason connect() does.
|
||||||
this._refreshBuffer();
|
this._refreshBuffer();
|
||||||
} else if (msg.t === 'ia') {
|
} else if (msg.t === 'ia') {
|
||||||
// Input ACK. The frame names no session, so it is this pane's.
|
// Input ACK. The frame names no session, so it is this pane's.
|
||||||
@@ -382,7 +376,7 @@
|
|||||||
this._onPtyGeometryReport(msg.c, msg.r);
|
this._onPtyGeometryReport(msg.c, msg.r);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* Malformed frame — ignore, matches primary pane's tolerance. */
|
/* Malformed frame: ignored, as in the primary pane. */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -397,7 +391,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = () => {
|
ws.onerror = () => {
|
||||||
// onclose fires after onerror — cleanup happens there.
|
// onclose fires after onerror: cleanup happens there.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,8 +401,8 @@
|
|||||||
if (!ws) return;
|
if (!ws) return;
|
||||||
ws.onopen = null;
|
ws.onopen = null;
|
||||||
ws.onmessage = null;
|
ws.onmessage = null;
|
||||||
// onclose fires asynchronously AFTER close(); without this it ran its
|
// onclose fires asynchronously AFTER close(); without this it would run
|
||||||
// "disconnected" write against a pane already torn down or replaced.
|
// its "disconnected" write against a pane already torn down or replaced.
|
||||||
ws.onclose = null;
|
ws.onclose = null;
|
||||||
ws.onerror = null;
|
ws.onerror = null;
|
||||||
try {
|
try {
|
||||||
@@ -575,7 +569,7 @@
|
|||||||
|
|
||||||
// Fetches and writes the session's current scrollback. Used both by
|
// Fetches and writes the session's current scrollback. Used both by
|
||||||
// connect() (initial load) and by the `{t:'r'}` server-refresh frame
|
// connect() (initial load) and by the `{t:'r'}` server-refresh frame
|
||||||
// (below) — the primary pane's own _onSessionNeedsRefresh (app.js) is
|
// (above). The primary pane's own _onSessionNeedsRefresh (app.js) is
|
||||||
// scoped to `this.activeSessionId` and clears/rewrites the primary
|
// scoped to `this.activeSessionId` and clears/rewrites the primary
|
||||||
// terminal, neither of which applies to this independent pane, so this is
|
// terminal, neither of which applies to this independent pane, so this is
|
||||||
// a standalone equivalent rather than a call into it.
|
// a standalone equivalent rather than a call into it.
|
||||||
@@ -584,10 +578,11 @@
|
|||||||
// _onSessionNeedsRefresh): a shell session can retain hundreds of
|
// _onSessionNeedsRefresh): a shell session can retain hundreds of
|
||||||
// thousands of plain scrollback lines, so pulling `?full=1` there parses
|
// thousands of plain scrollback lines, so pulling `?full=1` there parses
|
||||||
// an unbounded, server-capped (up to terminalBufferMaxBytes, 32MB) body
|
// an unbounded, server-capped (up to terminalBufferMaxBytes, 32MB) body
|
||||||
// into a 50000-line xterm on every load. Non-shell (TUI) sessions still
|
// into this xterm on every load; a shell loads the `tail=` window. A
|
||||||
// get one full replay. `fetch` here goes through the global wrapper
|
// non-shell (TUI) session gets one full replay, or the same bounded window
|
||||||
// (constants.js), which already prefixes CodemanBase — unlike the raw
|
// with `boundedLoad` (grid tiles). `fetch` here goes through the global
|
||||||
// WebSocket URL above, which does not.
|
// wrapper (constants.js), which already prefixes CodemanBase, unlike the
|
||||||
|
// raw WebSocket URL above, which does not.
|
||||||
//
|
//
|
||||||
// Single-flight: the flag is held across the fetch AND the chunked write
|
// Single-flight: the flag is held across the fetch AND the chunked write
|
||||||
// (writeChunked resolves after its last chunk), so two replays can never
|
// (writeChunked resolves after its last chunk), so two replays can never
|
||||||
@@ -721,20 +716,20 @@
|
|||||||
// Wheel-up at the top of a SHELL pane's scrollback. tmux repaints a burst of
|
// Wheel-up at the top of a SHELL pane's scrollback. tmux repaints a burst of
|
||||||
// output (`cat` of a file longer than the screen) instead of scrolling it,
|
// output (`cat` of a file longer than the screen) instead of scrolling it,
|
||||||
// so this pane's xterm ends up with about one screen of scrollback while
|
// so this pane's xterm ends up with about one screen of scrollback while
|
||||||
// tmux holds every line — and nothing here ever went back to ask, so the
|
// tmux holds every line, and without this pull the history is
|
||||||
// history was unreachable. The primary pane has the same pull
|
// unreachable. The primary pane has the same pull
|
||||||
// (app.js _maybeRefetchFullHistory); Pane B is a separate xterm and needs its
|
// (app.js _maybeRefetchFullHistory); a tile is a separate xterm and needs
|
||||||
// own. Shell only: a non-shell CLI's history is out of scope for this pull
|
// its own. Shell only: a non-shell CLI's history is out of scope for this
|
||||||
// (its load already takes `full=1`; codex and Claude's inline renderer do
|
// pull (its load already takes `full=1`; codex and Claude's inline renderer
|
||||||
// grow tmux history, this just isn't how they recover it). The alternate-
|
// do grow tmux history, this just isn't how they recover it). The
|
||||||
// screen skip (nano, vim, less) only matters for a direct-PTY shell — under
|
// alternate-screen skip (nano, vim, less) only matters for a direct-PTY
|
||||||
// tmux the browser xterm never enters the alternate buffer.
|
// shell: under tmux the browser xterm never enters the alternate buffer.
|
||||||
_maybeLoadMoreHistory() {
|
_maybeLoadMoreHistory() {
|
||||||
if (this.sessionMode !== 'shell' || this._destroyed || !this.terminal) return;
|
if (this.sessionMode !== 'shell' || this._destroyed || !this.terminal) return;
|
||||||
if (this._bufferLoading) return;
|
if (this._bufferLoading) return;
|
||||||
// Mirrors app.js _maybeRefetchFullHistory and this pane's own
|
// Mirrors app.js _maybeRefetchFullHistory and this pane's own
|
||||||
// _sendResize(): a detached session's own window already owns its PTY
|
// _sendResize(): a detached session's own window already owns its PTY
|
||||||
// size and scrollback, so Pane B has nothing of its own to reconcile.
|
// size and scrollback, so this pane has nothing of its own to reconcile.
|
||||||
if (this.detachedSessions?.has(this.sessionId)) return;
|
if (this.detachedSessions?.has(this.sessionId)) return;
|
||||||
const active = this.terminal.buffer.active;
|
const active = this.terminal.buffer.active;
|
||||||
if (active.type !== 'normal' || active.viewportY !== 0) return;
|
if (active.type !== 'normal' || active.viewportY !== 0) return;
|
||||||
@@ -795,7 +790,7 @@
|
|||||||
capturedAt = performance.now();
|
capturedAt = performance.now();
|
||||||
// Opened only now: a frame from before the response is either replaced by
|
// Opened only now: a frame from before the response is either replaced by
|
||||||
// the capture or written unchanged, so holding it for the round trip
|
// the capture or written unchanged, so holding it for the round trip
|
||||||
// bought nothing and froze the pane for as long as the fetch took.
|
// would buy nothing and freeze the pane for as long as the fetch took.
|
||||||
this._liveQueue = [];
|
this._liveQueue = [];
|
||||||
const payload = (await res.json())?.data;
|
const payload = (await res.json())?.data;
|
||||||
clearTimeout(abortTimer);
|
clearTimeout(abortTimer);
|
||||||
@@ -842,7 +837,7 @@
|
|||||||
if (delta > 0) this.terminal.scrollToLine(delta);
|
if (delta > 0) this.terminal.scrollToLine(delta);
|
||||||
else this.terminal.scrollToTop();
|
else this.terminal.scrollToTop();
|
||||||
} catch {
|
} catch {
|
||||||
/* Best-effort — live output keeps arriving whatever happens here. */
|
/* Best-effort: live output keeps arriving whatever happens here. */
|
||||||
} finally {
|
} finally {
|
||||||
clearTimeout(abortTimer);
|
clearTimeout(abortTimer);
|
||||||
this._loadAbort = null;
|
this._loadAbort = null;
|
||||||
@@ -880,7 +875,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The `{t:'r'}` server-refresh path: clear, then replay. Two refresh
|
// The `{t:'r'}` server-refresh path: clear, then replay. Two refresh
|
||||||
// frames in a row used to start two concurrent replays, each clearing
|
// frames in a row must not start two concurrent replays, each clearing
|
||||||
// the terminal under the other's chunked write. A refresh that arrives
|
// the terminal under the other's chunked write. A refresh that arrives
|
||||||
// mid-replay is COALESCED into one trailing re-run rather than ignored:
|
// mid-replay is COALESCED into one trailing re-run rather than ignored:
|
||||||
// the in-flight fetch may predate the drop the new frame is reporting,
|
// the in-flight fetch may predate the drop the new frame is reporting,
|
||||||
@@ -893,8 +888,8 @@
|
|||||||
void this._loadBuffer({ refresh: true });
|
void this._loadBuffer({ refresh: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Local reflow only — no PTY resize frame. Split out so a divider drag
|
// Local reflow only, no PTY resize frame. Split out so a divider drag
|
||||||
// can reflow both panes at the browser's paint rate (rAF) while sending
|
// can reflow the panes at the browser's paint rate (rAF) while sending
|
||||||
// the actual `{t:'z'}` resize once, at drag end, matching the primary
|
// the actual `{t:'z'}` resize once, at drag end, matching the primary
|
||||||
// pane's own convention (throttledResize in terminal-ui.js).
|
// pane's own convention (throttledResize in terminal-ui.js).
|
||||||
localFit() {
|
localFit() {
|
||||||
@@ -915,8 +910,8 @@
|
|||||||
if (!this._wsReady || !this.fitAddon || !this.terminal) return;
|
if (!this._wsReady || !this.fitAddon || !this.terminal) return;
|
||||||
// One PTY cannot hold two sizes (mirrors sendResize's own
|
// One PTY cannot hold two sizes (mirrors sendResize's own
|
||||||
// detachedElsewhere yield in terminal-ui.js): the session got detached
|
// detachedElsewhere yield in terminal-ui.js): the session got detached
|
||||||
// to its own window AFTER this split was opened, so its own window now
|
// to its own window AFTER this pane was opened, so its own window now
|
||||||
// owns the PTY's size and Pane B must stand aside.
|
// owns the PTY's size and this pane must stand aside.
|
||||||
if (this.detachedSessions?.has(this.sessionId)) return;
|
if (this.detachedSessions?.has(this.sessionId)) return;
|
||||||
// A hidden pane (a web tab over it, a zoomed neighbour) measures NaN, and
|
// A hidden pane (a web tab over it, a zoomed neighbour) measures NaN, and
|
||||||
// fit() then leaves the xterm alone: there is no size worth reporting.
|
// fit() then leaves the xterm alone: there is no size worth reporting.
|
||||||
@@ -924,9 +919,9 @@
|
|||||||
if (!dims || !Number.isFinite(dims.cols) || !Number.isFinite(dims.rows)) return;
|
if (!dims || !Number.isFinite(dims.cols) || !Number.isFinite(dims.rows)) return;
|
||||||
// Report what the xterm actually holds, so the PTY gets exactly the size
|
// Report what the xterm actually holds, so the PTY gets exactly the size
|
||||||
// the pane renders at. Unclamped, unlike the primary pane's 40x10 floor:
|
// the pane renders at. Unclamped, unlike the primary pane's 40x10 floor:
|
||||||
// a floor here misreported Pane B's width at the divider's reachable 20%
|
// a floor would misreport the split's Pane B at its divider's reachable
|
||||||
// position (about 28 columns), causing real output-wrapping bugs, and a
|
// 20% position (about 28 columns) and wrap output wrongly, and a floored
|
||||||
// floored xterm would be wider than its container. The server enforces
|
// xterm would be wider than its container. The server enforces
|
||||||
// its own valid range ([1,500]/[1,200] in ws-routes.ts).
|
// its own valid range ([1,500]/[1,200] in ws-routes.ts).
|
||||||
const cols = this.terminal.cols;
|
const cols = this.terminal.cols;
|
||||||
const rows = this.terminal.rows;
|
const rows = this.terminal.rows;
|
||||||
|
|||||||
Reference in New Issue
Block a user