Skip to content

fix(desktop): trigger TUI resize on reattach and focus#792

Merged
Kitenite merged 9 commits intomainfrom
opencode-test
Jan 18, 2026
Merged

fix(desktop): trigger TUI resize on reattach and focus#792
Kitenite merged 9 commits intomainfrom
opencode-test

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Jan 17, 2026

Summary

  • Add resize nudge (SIGWINCH) on session reattach for non-alt-screen terminals
  • Add resize nudge on pane focus change for alt-screen terminals
  • Apps with focus reporting (mode 1004) get CSI I from xterm.js automatically

Test plan

  • Open vim or htop in a terminal pane
  • Switch to another tab and back - verify TUI repaints correctly (reattach)
  • Split the pane and switch focus between panes - verify TUI repaints (focus change)

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 17, 2026

Warning

Rate limit exceeded

@Kitenite has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 36 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 1c50eaa and a8afc85.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx
📝 Walkthrough

Walkthrough

Enhanced the Terminal component's focus-handling effect to trigger a resize nudge sequence when the terminal pane gains focus in alternate screen mode. This uses requestAnimationFrame timing and delayed resizing to force SIGWINCH-based repaints for terminal user interfaces (TUIs). Added paneId to the effect dependency to scope behavior per tab.

Changes

Cohort / File(s) Summary
Terminal Focus & Resize Enhancement
apps/desktop/src/renderer/screens/main/components/.../Terminal/Terminal.tsx
Modified focus effect to detect alternate screen mode and trigger coordinated resize sequence (fit → down/up) via RAF and delayed resize. Requires xterm and FitAddon present. Added paneId dependency to effect. Expanded documentation around SIGWINCH fallback and focus reporting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • AviPeltz

Poem

🐰 A pane comes into view,
Focus signals rush through,
Resize nudges dance—
A SIGWINCH prance,
TUI repaints fresh and new!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The description covers the key changes and test plan, but omits required template sections like 'Related Issues', 'Type of Change', and 'Additional Notes', though these are non-critical.
Title check ✅ Passed The title 'fix(desktop): trigger TUI resize on reattach and focus' is partially related to the changeset. It mentions 'TUI resize' and 'focus', which are key aspects of the change, but 'reattach' is not accurately reflected in the actual implementation (which focuses on pane focus changes and alternate screen mode, not reattachment).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx`:
- Around line 999-1001: The resize nudge currently calls resizeRef.current({
paneId, cols, rows: rows - 1 }) even when rows === 1 which results in rows: 0
and may not trigger SIGWINCH; change the guard to only perform the down-nudge
when rows > 1, i.e., if (cols > 0 && rows > 1) then do the resizeRef.current
down-nudge and subsequent up-nudge. Also extract the 100 (ms) timeout into a
named module-level constant (e.g., RESIZE_NUDGE_TIMEOUT_MS) and use that
constant instead of the magic number in the setTimeout call so the meaning is
clear and follows coding guidelines.
🧹 Nitpick comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx (1)

1002-1007: Extract the 100ms nudge delay into a named constant.

Centralizing timing values improves maintainability and keeps the nudge behavior consistent.

♻️ Suggested refactor
+const FOCUS_RESIZE_NUDGE_DELAY_MS = 100;
-						setTimeout(() => {
+						setTimeout(() => {
 							if (xtermRef.current !== xterm) return;
 							resizeRef.current({ paneId, cols, rows });
 							// Force xterm to repaint after SIGWINCH completes
 							xterm.refresh(0, rows - 1);
-						}, 100);
+						}, FOCUS_RESIZE_NUDGE_DELAY_MS);

As per coding guidelines, extract magic numbers to named constants.

Comment on lines +999 to +1001
if (cols > 0 && rows > 0) {
// Resize down then up to guarantee SIGWINCH is sent to PTY
resizeRef.current({ paneId, cols, rows: rows - 1 });
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

xterm.js PTY resize minimum rows cols constraints specification

💡 Result:

Short answer

  • xterm.js itself does not impose a hard minimum for cols/rows — cols/rows can only be set in the constructor and can be changed at runtime with Terminal.resize(cols, rows). You should debounce resize calls so the PTY can process them. [1][2]

What actually matters for the running process

  • The PTY/window size is maintained in the kernel via struct winsize and the TIOCSWINSZ/TIOCGWINSZ ioctls; the kernel fields are unsigned shorts (ws_row, ws_col) and a value of 0 is treated as “undefined”. The kernel/PTY layer does not enforce an application-level minimum; programs or helper utilities often fall back to 80×24 when sizes are missing or zero. [3][4]

Practical constraints and gotchas

  • You must send the new size from the browser to your backend and call TIOCSWINSZ on the slave PTY; if you don’t, shells and full‑screen apps will continue to see the old/default (often 80×24). Many examples/tools (e.g., resize implementations) apply a minimum fallback like 80×24. [5][2][6]

References

  • xterm.js Terminal API (cols/rows, resize). [1]
  • xterm.js docs: cols/rows set only at constructor; Terminal.resize exists and debounce recommendation. [2]
  • TIOCSWINSZ / struct winsize (ws_row, ws_col; 0 = undefined). man pages. [3][4]
  • historical resize.c: common fallback to 80×24 when inputs are missing/too small. [5]

Sources
[1] xterm.js Terminal API docs.
[2] xterm.js class/docs pages (cols/rows options, Terminal.resize).
[3] man7.org TIOCSWINSZ/TIOCGWINSZ manual.
[4] tty(7) / winsize description (0 means undefined).
[5] example resize.c showing 80×24 fallback.


🏁 Script executed:

# First, let's examine the actual Terminal.tsx file to understand the context
find . -name "Terminal.tsx" -path "*/apps/desktop/src/renderer/*" | head -5

Repository: superset-sh/superset

Length of output: 175


🏁 Script executed:

# Search for resize-related code in the Terminal component
fd "Terminal.tsx" -path "*/apps/desktop/src/renderer/*" -exec cat -n {} + | head -100

Repository: superset-sh/superset

Length of output: 232


🏁 Script executed:

cat -n ./apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx | sed -n '990,1015p'

Repository: superset-sh/superset

Length of output: 995


🏁 Script executed:

# Get more context around the resize logic
cat -n ./apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx | sed -n '960,1020p'

Repository: superset-sh/superset

Length of output: 2607


🏁 Script executed:

# Search for any existing row/col validation in the file
rg "rows.*[<>]|cols.*[<>]" ./apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx -A 2 -B 2

Repository: superset-sh/superset

Length of output: 846


Guard resize nudge for rows > 1 and extract magic number timeout constant.

When rows === 1, the resize-down call sends rows: 0, which prevents the resize from actually changing dimensions and may not trigger SIGWINCH as intended. Additionally, the 100ms timeout is a magic number that should be extracted to a named constant at the module top per coding guidelines.

Suggested fix
+const RESIZE_NUDGE_TIMEOUT_MS = 100;

				if (cols > 0 && rows > 1) {
					// Resize down then up to guarantee SIGWINCH is sent to PTY
					resizeRef.current({ paneId, cols, rows: rows - 1 });
					setTimeout(() => {
						if (xtermRef.current === xterm) {
							resizeRef.current({ paneId, cols, rows });
							xterm.refresh(0, rows - 1);
						}
-				}, 100);
+				}, RESIZE_NUDGE_TIMEOUT_MS);
🤖 Prompt for AI Agents
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/Terminal/Terminal.tsx`
around lines 999 - 1001, The resize nudge currently calls resizeRef.current({
paneId, cols, rows: rows - 1 }) even when rows === 1 which results in rows: 0
and may not trigger SIGWINCH; change the guard to only perform the down-nudge
when rows > 1, i.e., if (cols > 0 && rows > 1) then do the resizeRef.current
down-nudge and subsequent up-nudge. Also extract the 100 (ms) timeout into a
named module-level constant (e.g., RESIZE_NUDGE_TIMEOUT_MS) and use that
constant instead of the magic number in the setTimeout call so the meaning is
clear and follows coding guidelines.

@Kitenite Kitenite force-pushed the opencode-test branch 2 times, most recently from 1e3cdb0 to 61b8764 Compare January 17, 2026 22:57
@Kitenite Kitenite changed the title fix(desktop): trigger TUI repaint on pane focus change fix(desktop): trigger TUI repaint on reattach and pane focus change Jan 17, 2026
When switching between panes within a tab, TUI apps in alternate screen
mode would show stale/blank content because they didn't know the terminal
regained focus.

Two mechanisms exist for TUI repaint:
1. Focus reporting (mode 1004): App sends ESC[?1004h, terminal sends CSI I
   on focus - handled automatically by xterm.js
2. SIGWINCH: Resize signal triggers repaint - needed for apps without
   focus reporting

Added resize nudge (same pattern as alt-screen reattach) when isFocused
changes for alt-screen terminals. This ensures TUIs like vim/htop repaint
correctly regardless of whether they use focus reporting.
The reattach cases are sufficient - pane focus change doesn't need
SIGWINCH since apps with focus reporting get CSI I automatically.
Pane focus change doesn't need SIGWINCH - apps with focus reporting
get CSI I automatically from xterm.js. Reattach cases still trigger
resize nudge for TUI repaint.
- Remove resize nudge pattern (resize down/up)
- Just call fitAddon.fit() on reattach after streaming is enabled
- Fixes race condition by ensuring resize happens after attach
@Kitenite Kitenite changed the title fix(desktop): trigger TUI repaint on reattach and pane focus change fix(desktop): trigger TUI resize Jan 18, 2026
@Kitenite Kitenite changed the title fix(desktop): trigger TUI resize fix(desktop): trigger TUI resize on reattach and focus Jan 18, 2026
@Kitenite Kitenite merged commit 2f59b9e into main Jan 18, 2026
5 checks passed
@Kitenite Kitenite deleted the opencode-test branch January 18, 2026 00:05
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

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.

1 participant