Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions apps/web/src/terminal/ghostty/surface.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,30 @@ describe("GhosttyTerminalSurface visibility", () => {
expect(resizePty).toHaveBeenCalledOnce();
});

it("sends the settled grid when a resize drag is released", async () => {
const harness = createHarness();
const onResize = vi.fn<(cols: number, rows: number) => void>();
const surface = await harness.create({ onResize });
vi.advanceTimersByTime(150);
onResize.mockClear();

// Mid-drag, the grid reflows locally while the PTY waits for the debounce.
harness.mount.clientWidth = 120;
harness.resize();
expect(surface.cols).toBe(14);
expect(onResize).not.toHaveBeenCalled();

// The release writes the final width before the observer reports it.
harness.mount.clientWidth = 96;
window.dispatchEvent(new Event("pointerup"));
expect(onResize).toHaveBeenCalledExactlyOnceWith(11, 6);

harness.resize();
vi.advanceTimersByTime(150);
window.dispatchEvent(new Event("pointerup"));
expect(onResize).toHaveBeenCalledOnce();
});

it("replays the current grid on reveal when a hidden host becomes ready", async () => {
const harness = createHarness();
const onResize = vi.fn<(cols: number, rows: number) => void>();
Expand Down
20 changes: 20 additions & 0 deletions apps/web/src/terminal/ghostty/surface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,22 @@ export class GhosttyTerminalSurface {
}, 150);
}

/**
* Releasing a panel, drawer, or split divider settles the size, so the PTY
* hears it now instead of after the debounce. Otherwise typing right after
* the release reaches a shell that still lays out the old width.
*/
private readonly onWindowPointerRelease = () => {
if (this.disposed || this.resizeNotifyTimer === null) return;
// The drag hook writes the release position before this bubbles to window,
// so fit now rather than wait a frame for the observer.
this.fit();
if (this.resizeNotifyTimer === null) return;
window.clearTimeout(this.resizeNotifyTimer);
this.resizeNotifyTimer = null;
this.options.onResize(this.cols, this.rows);
};

focus(): void {
if (this.disposed || !this.visible) return;
this.input.focus({ preventScroll: true });
Expand Down Expand Up @@ -1843,6 +1859,8 @@ export class GhosttyTerminalSurface {
this.scrollbar.addEventListener("pointerup", this.onScrollbarPointerUp);
this.scrollbar.addEventListener("pointercancel", this.onScrollbarPointerUp);
this.scrollbar.addEventListener("keydown", this.onScrollbarKeyDown);
window.addEventListener("pointerup", this.onWindowPointerRelease);
window.addEventListener("pointercancel", this.onWindowPointerRelease);
}

private removeEvents(): void {
Expand All @@ -1869,6 +1887,8 @@ export class GhosttyTerminalSurface {
this.scrollbar.removeEventListener("pointerup", this.onScrollbarPointerUp);
this.scrollbar.removeEventListener("pointercancel", this.onScrollbarPointerUp);
this.scrollbar.removeEventListener("keydown", this.onScrollbarKeyDown);
window.removeEventListener("pointerup", this.onWindowPointerRelease);
window.removeEventListener("pointercancel", this.onWindowPointerRelease);
}

private scrollViewport(deltaRows: number): void {
Expand Down
Loading