From feb37237536906434379344106a9ae733743c2d9 Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Fri, 10 Jul 2026 16:56:15 -0400 Subject: [PATCH] fix(terminal): preserve animation-frame first fit --- .superpowers/sdd/task-11-report.md | 9 +++++++ .../agent-workspace/terminal-fit.ts | 15 ++++++++--- apps/next/tests/unit/terminal-fit.test.ts | 27 +++++++++++++++++++ 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/.superpowers/sdd/task-11-report.md b/.superpowers/sdd/task-11-report.md index 57b55d3..e8cc3bb 100644 --- a/.superpowers/sdd/task-11-report.md +++ b/.superpowers/sdd/task-11-report.md @@ -22,3 +22,12 @@ Implemented terminal fit scheduling for the first animation frame, `document.fon - No implementation concerns found. - The component-suite worktree dependency-resolution issue remains external to this task. + +## Reviewer follow-up: preserve the animation-frame first fit + +- RED: `cd apps/next && bun run test:unit -- terminal-fit` exited 1 with 1 failed / 2 passed. The new `waits for the first animation frame before font-triggered fits` regression expected zero calls before rAF, but `fit` had already been called 2 times after flushing the resolved font promises. +- GREEN: after gating both font-triggered refits on the initial rAF fit promise, `cd apps/next && bun run test:unit -- terminal-fit` exited 0 with 1 file / 3 tests passed. +- Final focused verification: `cd apps/next && bun run test:unit -- terminal-fit` exited 0 with 1 file / 3 tests passed. +- Final full unit verification: `cd apps/next && bun run test:unit` exited 0 with 4 files / 11 tests passed. +- Final type verification: `cd apps/next && bun run typecheck` exited 0. +- Final formatting verification: `cd apps/next && bun run format` exited 0; all matched files use Prettier formatting. diff --git a/apps/next/src/components/agent-workspace/terminal-fit.ts b/apps/next/src/components/agent-workspace/terminal-fit.ts index da1d777..9cbeec3 100644 --- a/apps/next/src/components/agent-workspace/terminal-fit.ts +++ b/apps/next/src/components/agent-workspace/terminal-fit.ts @@ -12,10 +12,17 @@ export const scheduleTerminalFits = (deps: { deps.sendResize(); }; - deps.raf(refit); - void deps.fontsReady.then(refit).catch(() => undefined); - void deps - .loadNerdFont() + const initialFit = new Promise((resolve) => { + deps.raf(() => { + refit(); + resolve(); + }); + }); + + void Promise.all([deps.fontsReady, initialFit]) + .then(refit) + .catch(() => undefined); + void Promise.all([deps.loadNerdFont(), initialFit]) .then(refit) .catch(() => undefined); }; diff --git a/apps/next/tests/unit/terminal-fit.test.ts b/apps/next/tests/unit/terminal-fit.test.ts index 5abc592..cc6fffa 100644 --- a/apps/next/tests/unit/terminal-fit.test.ts +++ b/apps/next/tests/unit/terminal-fit.test.ts @@ -7,6 +7,33 @@ const flush = async () => { }; describe('scheduleTerminalFits', () => { + test('waits for the first animation frame before font-triggered fits', async () => { + const fit = vi.fn(); + const sendResize = vi.fn(); + let rafCb: (() => void) | undefined; + + scheduleTerminalFits({ + fit, + sendResize, + fontsReady: Promise.resolve(), + loadNerdFont: () => Promise.resolve(), + raf: (cb) => { + rafCb = cb; + }, + isAborted: () => false, + }); + await flush(); + + expect(fit).not.toHaveBeenCalled(); + expect(sendResize).not.toHaveBeenCalled(); + + rafCb?.(); + await flush(); + + expect(fit).toHaveBeenCalledTimes(3); + expect(sendResize).toHaveBeenCalledTimes(3); + }); + test('fits in rAF and again after fonts.ready and nerd-font load', async () => { const fit = vi.fn(); const sendResize = vi.fn();