mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
perf(resize): a window resize fits each grid tile once, not twice
A window resize reached every grid tile twice: the grid's own
ResizeObserver refits them (tile-grid.js _scheduleTileGridRefit, 150 ms
trailing), then the main terminal's trailing resize timer (terminal-ui.js
throttledResize, 300 ms) ran _forEachTile(fit) over them again. The second
pass re-measured six panes and sent nothing (_lastSentDims dedupes the PTY
side). The timer now refits the split's Pane B only ({ grid: false }); grid
tiles exist only while the grid owns the terminal, and then its observer
already covers them.
Measured (6 tiles, 20-step window resize and back, headless, tileperf):
fit() 12 -> 6 per resize burst; PTY resizes 6 -> 6; browser layouts
unchanged within noise (85/75 -> 92/71), so this removes wasted calls only.
Live, this code (6 tiles, tileperf): a 20-step window resize, and the resize
back, each ran fit() 6 times and sent 6 PTY resizes (12 and 6 before).
Test: the timer's one _forEachTile call passes { grid: false } (it lives
inside initTerminal, so read from source like the #464 geometry tests);
mutation-checked.
Scope: PR 2. The line is in terminal-ui.js (a PR 1 seam), but on PR 1 alone
_forEachTile reaches only the split's Pane B: the double refit needs the grid.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1428,8 +1428,11 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
// frame — this observer only ever measured Pane A's container, so
|
// frame — this observer only ever measured Pane A's container, so
|
||||||
// without this call Pane B never learned about a window resize, an
|
// without this call Pane B never learned about a window resize, an
|
||||||
// Alt+B sidebar toggle, or a tab-rail drag, and its PTY silently
|
// Alt+B sidebar toggle, or a tab-rail drag, and its PTY silently
|
||||||
// stayed at whatever size it was last dragged to.
|
// stayed at whatever size it was last dragged to. Grid tiles are left
|
||||||
this._forEachTile?.((tile) => tile.fit());
|
// out: the grid's own observer (tile-grid.js _scheduleTileGridRefit)
|
||||||
|
// refits every one of them on the same resize, and a second fit here
|
||||||
|
// only re-measured six panes to send nothing.
|
||||||
|
this._forEachTile?.((tile) => tile.fit(), { grid: false });
|
||||||
}, 300); // Trailing-edge: only fire after 300ms of no resize events
|
}, 300); // Trailing-edge: only fire after 300ms of no resize events
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
* tile-grid.js in one `vm` context, with the shared fake DOM and fake
|
* tile-grid.js in one `vm` context, with the shared fake DOM and fake
|
||||||
* TerminalTile (test/mocks/tile-grid-vm.ts). Port: N/A.
|
* TerminalTile (test/mocks/tile-grid-vm.ts). Port: N/A.
|
||||||
*/
|
*/
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import {
|
import {
|
||||||
FakeTile,
|
FakeTile,
|
||||||
@@ -357,6 +359,22 @@ describe('panes and fonts while the grid is open', () => {
|
|||||||
expect(pane.sessionId).toBe('s-a');
|
expect(pane.sessionId).toBe('s-a');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("the main terminal's window-resize timer leaves grid tiles to the grid's own observer", () => {
|
||||||
|
// A window resize fires both: the grid's ResizeObserver refits every tile
|
||||||
|
// (tile-grid.js _scheduleTileGridRefit) and the main terminal's trailing
|
||||||
|
// timer used to refit them again, twelve fit() calls for six tiles and
|
||||||
|
// nothing more sent (measured). The timer lives inside initTerminal(), so
|
||||||
|
// this reads its source; the split's Pane B is still refitted there.
|
||||||
|
const src = readFileSync(resolve(import.meta.dirname, '../src/web/public/terminal-ui.js'), 'utf8');
|
||||||
|
const start = src.indexOf('const throttledResize = () => {');
|
||||||
|
const end = src.indexOf("window.addEventListener('resize', throttledResize)", start);
|
||||||
|
expect(start).toBeGreaterThan(-1);
|
||||||
|
expect(end).toBeGreaterThan(start);
|
||||||
|
const calls = src.slice(start, end).match(/this\._forEachTile\?\.\([^;]*;/g) ?? [];
|
||||||
|
expect(calls).toHaveLength(1);
|
||||||
|
expect(calls[0]).toContain('{ grid: false }');
|
||||||
|
});
|
||||||
|
|
||||||
it('_forEachTile reaches every grid tile; { grid: false } skips them', () => {
|
it('_forEachTile reaches every grid tile; { grid: false } skips them', () => {
|
||||||
const app = appWithGrid(true);
|
const app = appWithGrid(true);
|
||||||
const seen: string[] = [];
|
const seen: string[] = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user