fix: restore terminal redraw after tab reactivation (#11275)

This commit is contained in:
Utkarsh Adhran
2026-06-08 16:36:57 +02:00
committed by GitHub
parent 9c579c0f0f
commit 5ddcbe303f
4 changed files with 72 additions and 15 deletions
@@ -9,6 +9,7 @@ import { BaseSession } from '../session'
import { Frontend } from '../frontends/frontend'
import { XTermFrontend, XTermWebGLFrontend } from '../frontends/xtermFrontend'
import { syncTerminalVisibility } from '../frontends/visibility'
import { ResizeEvent, BaseTerminalProfile } from './interfaces'
import { TerminalDecorator } from './decorator'
import { SearchPanelComponent } from '../components/searchPanel.component'
@@ -446,17 +447,7 @@ export class BaseTerminalTabComponent<P extends BaseTerminalProfile> extends Bas
.pipe(debounce(visibility => interval(visibility ? 0 : INACTIVE_TAB_UNLOAD_DELAY)))
.subscribe(visibility => {
if (this.frontend instanceof XTermFrontend) {
if (visibility) {
// this.frontend.resizeHandler()
const term = this.frontend.xterm as any
term._core._renderService?.clear()
term._core._renderService?.handleResize(term.cols, term.rows)
} else {
this.frontend.xterm.element?.querySelectorAll('canvas').forEach(c => {
c.height = c.width = 0
c.style.height = c.style.width = '0px'
})
}
syncTerminalVisibility(this.frontend, visibility)
}
})
}
@@ -0,0 +1,38 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { syncTerminalVisibility } from './visibility.ts'
test('syncTerminalVisibility reactivates the frontend when the tab becomes visible', () => {
let reactivated = 0
let deactivated = 0
syncTerminalVisibility({
reactivateAfterVisibilityChange: () => {
reactivated++
},
deactivateAfterVisibilityChange: () => {
deactivated++
},
}, true)
assert.equal(reactivated, 1)
assert.equal(deactivated, 0)
})
test('syncTerminalVisibility releases hidden-tab resources when the tab becomes invisible', () => {
let reactivated = 0
let deactivated = 0
syncTerminalVisibility({
reactivateAfterVisibilityChange: () => {
reactivated++
},
deactivateAfterVisibilityChange: () => {
deactivated++
},
}, false)
assert.equal(reactivated, 0)
assert.equal(deactivated, 1)
})
@@ -0,0 +1,12 @@
export interface VisibilityManagedTerminalFrontend {
reactivateAfterVisibilityChange: () => void
deactivateAfterVisibilityChange: () => void
}
export function syncTerminalVisibility (frontend: VisibilityManagedTerminalFrontend, visible: boolean): void {
if (visible) {
frontend.reactivateAfterVisibilityChange()
} else {
frontend.deactivateAfterVisibilityChange()
}
}
+20 -4
View File
@@ -98,15 +98,20 @@ export class XTermFrontend extends Frontend {
this.hostApp = injector.get(HostAppService)
this.themes = injector.get(ThemesService)
this.xterm = new Terminal({
const terminalOptions = {
allowTransparency: true,
allowProposedApi: true,
overviewRulerWidth: 8,
windowsPty: process.platform === 'win32' ? {
backend: this.configService.store.terminal.useConPTY ? 'conpty' : 'winpty',
backend: this.configService.store.terminal.useConPTY ? 'conpty' as const : 'winpty' as const,
buildNumber: getWindows10Build(),
} : undefined,
})
}
;(terminalOptions as Record<string, unknown>).overviewRuler = {
width: 8,
showBottomBorder: false,
showTopBorder: false,
}
this.xterm = new Terminal(terminalOptions)
this.flowControl = new FlowControl(this.xterm)
this.xtermCore = this.xterm['_core']
@@ -360,6 +365,17 @@ export class XTermFrontend extends Frontend {
delete this.resizeObserver
}
reactivateAfterVisibilityChange (): void {
this.resizeHandler()
}
deactivateAfterVisibilityChange (): void {
this.xterm.element?.querySelectorAll('canvas').forEach(c => {
c.height = c.width = 0
c.style.height = c.style.width = '0px'
})
}
destroy (): void {
super.destroy()
this.webGLAddon?.dispose()