mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-20 09:09:35 +02:00
fix(remote): reset SGR where the remote history meets the remote screen rows (#609 merge-time fix)
#609 keeps remote screen rows between the remote history and the local frame. Those screen rows come from a separate capture-pane in the remote command list, encoded from the default state, so the history's last row could leave a background or dim active over them. The history now ends with a reset before the screen rows, as the rows above the frame already do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
239f15437b
commit
aca358126d
2 files changed
+22
-5
No files matched your search
+6
-1
@@ -776,7 +776,12 @@ export function joinScrollbackAndFrame(scrollback: string | undefined, frame: st
|
||||
if (screen) {
|
||||
const screenRows = captureRows(screen);
|
||||
const frameStart = findFrameInScreen(screenRows, captureRows(frame));
|
||||
if (frameStart > 0) above = above.concat(screenRows.slice(0, frameStart));
|
||||
if (frameStart > 0) {
|
||||
// The remote screen is a capture of its own too, encoded from the default
|
||||
// state: the history's last row must not leave its attributes active.
|
||||
if (above.length > 0) above[above.length - 1] += '\x1b[0m';
|
||||
above = above.concat(screenRows.slice(0, frameStart));
|
||||
}
|
||||
}
|
||||
// Separate captures: tmux encodes the frame relative to the DEFAULT state, so
|
||||
// the rows above it must not leave their attributes active.
|
||||
|
||||
@@ -321,25 +321,27 @@ describe('joinScrollbackAndFrame with the remote screen', () => {
|
||||
const screen = rows('s1', 's2', 's3', 's4', 's5', 's6', 's7', '$ make');
|
||||
// Three more rows of output arrived: s1..s3 scrolled out of the frame.
|
||||
const frame = rows('s4', 's5', 's6', 's7', '$ make', 'out 1', 'out 2', 'out 3');
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(`${H}${rows('s1', 's2', 's3')}`)}${frame}`);
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(H)}${reset(rows('s1', 's2', 's3'))}${frame}`);
|
||||
});
|
||||
|
||||
it('the top of a remote screen taller than this pane is kept', () => {
|
||||
const screen = rows('t1', 't2', 's1', 's2', 's3', '$ ');
|
||||
const frame = rows('s1', 's2', 's3', '$ ');
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(`${H}${rows('t1', 't2')}`)}${frame}`);
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(H)}${reset(rows('t1', 't2'))}${frame}`);
|
||||
});
|
||||
|
||||
it('the last content row may have changed (output in flight) and still matches', () => {
|
||||
const screen = rows('s1', 's2', 's3', 's4', '$ par');
|
||||
const frame = rows('s2', 's3', 's4', '$ partial line done', 'next');
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(`${H}${rows('s1')}`)}${frame}`);
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(H)}${reset(rows('s1'))}${frame}`);
|
||||
});
|
||||
|
||||
it('compares text only: two tmuxes may encode the same attributes differently', () => {
|
||||
const screen = rows('\x1b[38;5;1ms1\x1b[39m', 's2 ', 's3', 's4', '$ ');
|
||||
const frame = rows('s2', '\x1b[1ms3\x1b[0m', 's4', '$ ', 'new');
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(`${reset(`${H}${rows('\x1b[38;5;1ms1\x1b[39m')}`)}${frame}`);
|
||||
expect(joinScrollbackAndFrame(H, frame, screen)).toBe(
|
||||
`${reset(H)}${reset(rows('\x1b[38;5;1ms1\x1b[39m'))}${frame}`
|
||||
);
|
||||
});
|
||||
|
||||
it('no overlap (more scrolled than the screen held) falls back to scrollback over frame', () => {
|
||||
@@ -373,6 +375,16 @@ describe('joinScrollbackAndFrame with the remote screen', () => {
|
||||
expect(findFrameInScreen(['a', 'x', '', '', 'z'], ['x', '', '', 'z', 'q'])).toBe(-1);
|
||||
});
|
||||
|
||||
it('resets SGR where the history meets the screen rows kept above the frame', () => {
|
||||
// Two captures in the remote command list: the history's last row ends with a
|
||||
// background set, and the screen rows are encoded from the default state.
|
||||
const screen = rows('s1', 's2', 's3', 's4', '$ ');
|
||||
const frame = rows('s2', 's3', 's4', '$ ', 'new');
|
||||
expect(joinScrollbackAndFrame('old\n\x1b[41mRED\n', frame, screen)).toBe(
|
||||
`old\n\x1b[41mRED\x1b[0m\n${reset(rows('s1'))}${frame}`
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps a scrollback that is one blank row, as the plain join did', () => {
|
||||
expect(joinScrollbackAndFrame('\n', 'frame\n')).toBe('\x1b[0m\nframe\n');
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user