Skip to content

fix resizing issue#112

Open
groninge01 wants to merge 4 commits into
proxysoul:mainfrom
groninge01:issue-91
Open

fix resizing issue#112
groninge01 wants to merge 4 commits into
proxysoul:mainfrom
groninge01:issue-91

Conversation

@groninge01

@groninge01 groninge01 commented Jun 12, 2026

Copy link
Copy Markdown

What

fixes #91

Why

see #91

Scope check

  • I read CONTRIBUTING.md and this PR is in the In scope table.
  • This PR does not touch any path in the Out of scope list.
  • One feature or fix per PR (no drive-by refactors).
  • bun run lint:fix && bun run format && bun run typecheck passes.
  • If behavior changed, a test was added or updated.

Verification

test

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

UI/Chat

  • src/core/terminal/ — Added ResizeAwareRenderer interface and setupTerminalResize() function to handle terminal window resizing via DEC mode 2048 sequences, stdout resize events, SIGWINCH signals, and polling fallback (fixes issue #91)
  • src/index.tsx — Refactored inline resize logic into dedicated handler module call
Author Files +Lines −Lines
groninge01 3 278 26

Walkthrough

Terminal resize detection is extracted from inline CLI startup code into a dedicated setupTerminalResize module. It registers four redundant mechanisms—DEC mode 2048 in-band CSI sequences, stdout resize events, SIGWINCH signals, and 200ms polling—to keep renderer dimensions synchronized with terminal size changes across different terminal types.

Changes

Terminal resize handler

Layer / File(s) Summary
Resize handler contract and mechanisms
src/core/terminal/resize-handler.ts
ResizeAwareRenderer interface defines terminal dimensions and input handler registration. Implementation registers four resize detection paths: CSI 48 in-band parsing, stdout "resize" listener (when TTY), SIGWINCH signal handler with delayed dimension read, and 200ms polling interval fallback. Enables DEC mode 2048 on startup and returns cleanup function that removes listeners and disables mode.
CLI startup integration
src/index.tsx
Import setupTerminalResize and call it during renderer initialization, replacing the previous inline resize setup logic (24 lines removed).
Comprehensive test coverage
tests/terminal-resize.test.ts
Tests verify DEC mode enable/disable sequences, input handler CSI 48 escape sequence parsing with correct column/row extraction, stdout resize listener triggering only on dimension changes, SIGWINCH delayed invocation via fake timers, polling interval detection and update behavior, and cleanup listener removal and mode disable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

Resize, resize, the terminal cries,
Four methods stand, watching the skies—
Escape codes, events, and signals too,
A polling guard when all else is through. 🔄


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error)

Check name Status Explanation Resolution
Title check ❌ Error Title violates Conventional Commits format: missing type prefix (feat/fix), scope, and exceeds clarity threshold with vague wording. Use format 'fix(terminal): handle window resize events' or similar—include type, scope, and specific descriptive action under 72 characters.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed Description references issue #91 with scope checklist completed and test verification provided; clearly related to the changeset.
Linked Issues check ✅ Passed Changeset implements resize synchronization across DEC mode 2048, stdout resize events, SIGWINCH handling, and polling fallback—directly addressing #91's window resize adaptation requirement.
Out of Scope Changes check ✅ Passed All changes confined to src/core/terminal/resize-handler.ts, src/index.tsx, and tests/terminal-resize.test.ts; no modifications to intelligence, agents, prompts, memory, compaction, hearth internals, or new tools/agents.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@groninge01 groninge01 marked this pull request as ready for review June 12, 2026 07:23
@groninge01 groninge01 requested a review from proxysoul as a code owner June 12, 2026 07:23

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/index.tsx`:
- Line 417: The call to setupTerminalResize(r) returns a cleanup function that
is currently discarded; capture that return (e.g., const terminalResizeCleanup =
setupTerminalResize(r)) and then invoke terminalResizeCleanup() inside
runCleanup() before calling restoreTerminal(); this ensures event listeners are
removed, the polling interval cleared, and DEC mode 2048 disabled without
leaking or duplicating cleanup logic from restoreTerminal().

In `@tests/terminal-resize.test.ts`:
- Around line 59-65: The test's process mock doesn't replace module-level
references because resize-handler.ts captures process.stdout on import; update
the code so setupTerminalResize (in resize-handler.ts) accepts a stdout/process
argument (e.g., add an optional parameter like stdout or process) and use that
value instead of referencing process.stdout directly, then change the tests to
pass the mockProcess/stdout into setupTerminalResize; alternatively, if you
prefer not to change production API, change the tests to dynamically import the
module after installing the mock (use await import('./resize-handler') in
beforeEach) so the module captures the mocked process—refer to
setupTerminalResize and resize-handler.ts when making the change.
- Around line 30-40: Replace the multiple `: any` test stubs with properly typed
mocks: define explicit mock interfaces (e.g., MockProcess and MockStdout) that
declare only the properties/methods your tests use, then change the declarations
for mockProcess, mockStdout, origProcessStdout, origProcessOn, and
origProcessRemoveListener to those types (or to `unknown` plus narrow with small
type-guard functions before use); remove the `: any` annotations and the
biome-ignore comments, and update any usages to use the mock interfaces or the
type-guarded values so the test compiles under strict mode and Bun's test
runtime.
- Line 1: Replace Vitest imports and APIs with Bun's test runner and mocks:
change the top-level import to use "bun:test" (describe, test, expect,
beforeEach, afterEach) and remove any Vitest-specific import of vi; replace all
vi.fn() usages with Bun.mock() or simple manual stubs, and replace
vi.useFakeTimers()/vi.clearAllTimers()/vi.advanceTimersByTime() with Bun's timer
controls or manual timer stubbing; also replace
vi.restoreAllMocks()/vi.resetAllMocks() with equivalent manual cleanup in
beforeEach/afterEach. Locate and update occurrences of vi.* in this file (e.g.,
any calls to vi.fn, vi.useFakeTimers, vi.restoreAllMocks, vi.clearAllTimers,
vi.advanceTimersByTime) and ensure tests run under Bun by using Bun-compatible
mocks and timer management.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b1c804fe-2a1d-4f7b-9601-1582f90fd22e

📥 Commits

Reviewing files that changed from the base of the PR and between 5bf13b1 and 1c13d44.

📒 Files selected for processing (3)
  • src/core/terminal/resize-handler.ts
  • src/index.tsx
  • tests/terminal-resize.test.ts

Comment thread src/index.tsx
} catch {}
}, 1000);
resizePoll.unref?.();
setupTerminalResize(r);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Store and invoke the cleanup function.

src/index.tsx:417 — setupTerminalResize(r) returns a cleanup function that removes event listeners, clears the polling interval, and disables DEC mode 2048. Return value is discarded, so listeners accumulate and the interval continues until process exit. Violates the function contract and duplicates the DEC mode disable logic already in restoreTerminal() line 46.

Store the cleanup function and call it in runCleanup() before restoreTerminal().

Proposed fix
+let cleanupTerminalResize: (() => void) | null = null;
+
 export async function start(opts: StartOptions): Promise<void> {
   const r = await opts.createCliRenderer({
     // ... config ...
   });
   
   if (process.stdout.isTTY) {
-    setupTerminalResize(r);
+    cleanupTerminalResize = setupTerminalResize(r);
   }
   
   // ... rest of start function ...
 }

Then in runCleanup() after line 54:

 function runCleanup(): void {
   if (cleanedUp) return;
   cleanedUp = true;
+  try {
+    cleanupTerminalResize?.();
+  } catch {}
   restoreTerminal();
   // ... rest of cleanup ...
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/index.tsx` at line 417, The call to setupTerminalResize(r) returns a
cleanup function that is currently discarded; capture that return (e.g., const
terminalResizeCleanup = setupTerminalResize(r)) and then invoke
terminalResizeCleanup() inside runCleanup() before calling restoreTerminal();
this ensures event listeners are removed, the polling interval cleared, and DEC
mode 2048 disabled without leaking or duplicating cleanup logic from
restoreTerminal().

@@ -0,0 +1,192 @@
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Reject: tests must use Bun's native runner, not vitest.

tests/** requires import { describe, expect, test } from "bun:test" per CONTRIBUTING.md. Vitest imports will fail CI.

Bun provides mock() for spies. Rework needed to replace vi.fn(), vi.useFakeTimers(), etc. with Bun equivalents or manual stubs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/terminal-resize.test.ts` at line 1, Replace Vitest imports and APIs
with Bun's test runner and mocks: change the top-level import to use "bun:test"
(describe, test, expect, beforeEach, afterEach) and remove any Vitest-specific
import of vi; replace all vi.fn() usages with Bun.mock() or simple manual stubs,
and replace vi.useFakeTimers()/vi.clearAllTimers()/vi.advanceTimersByTime() with
Bun's timer controls or manual timer stubbing; also replace
vi.restoreAllMocks()/vi.resetAllMocks() with equivalent manual cleanup in
beforeEach/afterEach. Locate and update occurrences of vi.* in this file (e.g.,
any calls to vi.fn, vi.useFakeTimers, vi.restoreAllMocks, vi.clearAllTimers,
vi.advanceTimersByTime) and ensure tests run under Bun by using Bun-compatible
mocks and timer management.

Source: Coding guidelines

Comment on lines +30 to +40
// biome-ignore lint/suspicious/noExplicitAny: test-only process stub
let mockProcess: any;
// biome-ignore lint/suspicious/noExplicitAny: test-only stdout stub
let mockStdout: any;

// biome-ignore lint/suspicious/noExplicitAny: test-only global replacement
let origProcessStdout: any;
// biome-ignore lint/suspicious/noExplicitAny: test-only global replacement
let origProcessOn: any;
// biome-ignore lint/suspicious/noExplicitAny: test-only global replacement
let origProcessRemoveListener: any;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Multiple : any annotations violate strict mode.

CONTRIBUTING.md: "reject : any and as any (strict mode — use Zod inference or proper types)". biome-ignore comments don't override repo policy. Use unknown with type guards or define explicit mock types.

Since this file needs rework for bun:test anyway, address both together.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/terminal-resize.test.ts` around lines 30 - 40, Replace the multiple `:
any` test stubs with properly typed mocks: define explicit mock interfaces
(e.g., MockProcess and MockStdout) that declare only the properties/methods your
tests use, then change the declarations for mockProcess, mockStdout,
origProcessStdout, origProcessOn, and origProcessRemoveListener to those types
(or to `unknown` plus narrow with small type-guard functions before use); remove
the `: any` annotations and the biome-ignore comments, and update any usages to
use the mock interfaces or the type-guarded values so the test compiles under
strict mode and Bun's test runtime.

Source: Coding guidelines

Comment on lines +59 to +65
origProcessStdout = process.stdout;
origProcessOn = process.on;
origProcessRemoveListener = process.removeListener;

// biome-ignore lint/suspicious/noExplicitAny: test-only process stub
(globalThis as any).process = mockProcess;
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Process mocking won't intercept module-level references.

resize-handler.ts accesses process.stdout directly. By the time beforeEach replaces globalThis.process, the module has already imported and cached the real process reference. The mock won't be used.

Either:

  1. Use dependency injection (pass stdout/process to setupTerminalResize)
  2. Use Bun's module mocking if available
  3. Re-import the module after mocking with dynamic import()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/terminal-resize.test.ts` around lines 59 - 65, The test's process mock
doesn't replace module-level references because resize-handler.ts captures
process.stdout on import; update the code so setupTerminalResize (in
resize-handler.ts) accepts a stdout/process argument (e.g., add an optional
parameter like stdout or process) and use that value instead of referencing
process.stdout directly, then change the tests to pass the mockProcess/stdout
into setupTerminalResize; alternatively, if you prefer not to change production
API, change the tests to dynamically import the module after installing the mock
(use await import('./resize-handler') in beforeEach) so the module captures the
mocked process—refer to setupTerminalResize and resize-handler.ts when making
the change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI does not follow window resizing

1 participant