mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
fix(tiles): a tile that joined before its pane existed resends its size
A session Run makes while the grid is open joins as a tile right away, so the tile connects and sends its size before Run starts the pane. The server only records a resize for a session with no PTY and spawns the pane at 120x40, and Run's own resize step measures the parked main terminal (display: none, so nothing). Measured live for Shell and Claude: a 97x17 tile over a 120x40 pane, for good (#464). The chrome refresh now remembers each tile's last-seen pid and calls TerminalTile.paneStarted() when it appears or changes. paneStarted() forgets the sent size and sends it; a hidden tile (a zoomed neighbour) sends nothing and keeps it forgotten, so its next fit() sends it, which a plain fit({ force: true }) would lose. Keyed on the sessions map, so a handleInit after an SSE drop counts too. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -134,6 +134,7 @@ export class FakeTile {
|
||||
reconnectNow = vi.fn();
|
||||
fit = vi.fn();
|
||||
localFit = vi.fn();
|
||||
paneStarted = vi.fn();
|
||||
destroy = vi.fn(() => {
|
||||
this._destroyed = true;
|
||||
});
|
||||
|
||||
@@ -583,6 +583,39 @@ describe('TerminalTile geometry (#464: the pane and its PTY never disagree)', ()
|
||||
expect(resizeFrames(ws).at(-1)).toEqual({ t: 'z', c: 28, r: 30, v: 'desktop' });
|
||||
});
|
||||
|
||||
it('paneStarted() resends an unchanged size: the first one went out before there was a PTY', async () => {
|
||||
const { tile, ws } = await connectTile(makeApp());
|
||||
ws.open();
|
||||
tile.paneStarted();
|
||||
expect(resizeFrames(ws)).toEqual([
|
||||
{ t: 'z', c: 80, r: 24, v: 'desktop' },
|
||||
{ t: 'z', c: 80, r: 24, v: 'desktop' },
|
||||
]);
|
||||
// Only once: the size is recorded again, so a plain fit does not repeat it.
|
||||
tile.fit();
|
||||
expect(resizeFrames(ws)).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('paneStarted() on a hidden tile sends nothing, and its next fit sends the size', async () => {
|
||||
const { tile, ws } = await connectTile(makeApp());
|
||||
ws.open();
|
||||
FakeFit.proposed = { cols: NaN, rows: NaN };
|
||||
tile.paneStarted();
|
||||
expect(resizeFrames(ws)).toHaveLength(1);
|
||||
// Shown again (a zoom ends) at the same size it had: still sent.
|
||||
FakeFit.proposed = { cols: 80, rows: 24 };
|
||||
tile.fit();
|
||||
expect(resizeFrames(ws)).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('paneStarted() before the socket opens sends nothing; the open sends the size once', async () => {
|
||||
const { tile, ws } = await connectTile(makeApp());
|
||||
tile.paneStarted();
|
||||
expect(resizeFrames(ws)).toHaveLength(0);
|
||||
ws.open();
|
||||
expect(resizeFrames(ws)).toEqual([{ t: 'z', c: 80, r: 24, v: 'desktop' }]);
|
||||
});
|
||||
|
||||
it('reports nothing while hidden (the fit addon measures NaN)', async () => {
|
||||
const { tile, ws, term } = await connectTile(makeApp());
|
||||
ws.open();
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
* `session:created` and never join. A grid already holding what the window
|
||||
* fits does not take it: Run's selection then shows it alone, with a hint.
|
||||
*
|
||||
* A tile that joined before its pane existed resends its size once the pid
|
||||
* appears (the server spawned the pane at its own default size).
|
||||
*
|
||||
* A tile's + also offers "New session in this case": the normal Run for the
|
||||
* case the tile's session belongs to, the toolbar's case put back afterwards.
|
||||
*
|
||||
@@ -20,6 +23,7 @@ import { resolve } from 'node:path';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
FakeEl,
|
||||
FakeTile,
|
||||
body,
|
||||
bySelector,
|
||||
makeGridApp,
|
||||
@@ -95,6 +99,45 @@ describe('Run from this tab', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('a pane that starts after its tile connected (#464)', () => {
|
||||
// The tile sent its size before there was a PTY; the server dropped it and
|
||||
// spawned the pane at its default size, so the size must go out again.
|
||||
const tileOf = (id: string) => FakeTile.all.filter((t) => t.sessionId === id).at(-1)!;
|
||||
const setPid = (app: GridApp, id: string, pid: number | null) =>
|
||||
app.sessions.set(id, { ...app.sessions.get(id), pid });
|
||||
|
||||
it('resends the size once when the pid appears, and again only for a new PTY', () => {
|
||||
const app = makeGridApp(IDS);
|
||||
app.openTileGrid(IDS);
|
||||
addSession(app, 's-new');
|
||||
app._joinTileGridFromRun('s-new');
|
||||
const tile = tileOf('s-new');
|
||||
app._renderTileChrome();
|
||||
expect(tile.paneStarted).not.toHaveBeenCalled();
|
||||
|
||||
setPid(app, 's-new', 4242);
|
||||
app._renderTileChrome();
|
||||
expect(tile.paneStarted).toHaveBeenCalledTimes(1);
|
||||
app._renderTileChrome();
|
||||
expect(tile.paneStarted).toHaveBeenCalledTimes(1);
|
||||
|
||||
// The pane went away and a new one started (an Attach, a respawned pane).
|
||||
setPid(app, 's-new', null);
|
||||
app._renderTileChrome();
|
||||
setPid(app, 's-new', 4343);
|
||||
app._renderTileChrome();
|
||||
expect(tile.paneStarted).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('a tile made for a session that already runs never asks', () => {
|
||||
const app = makeGridApp(IDS);
|
||||
app.openTileGrid(IDS);
|
||||
app._renderTileChrome();
|
||||
app._renderTileChrome();
|
||||
for (const id of IDS) expect(tileOf(id).paneStarted).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('+ / New session in this case', () => {
|
||||
function openMenu(app: GridApp, fromId: string) {
|
||||
app.openTileAddMenu({ stopPropagation: vi.fn(), preventDefault: vi.fn(), currentTarget: null }, fromId);
|
||||
|
||||
Reference in New Issue
Block a user