@@ -127,16 +127,27 @@ async function createSession(options) {
127127 let tmpDir = join ( tmpdir ( ) , `tui-driver-${ sessionId } ` )
128128 await mkdir ( tmpDir , { recursive : true } )
129129
130- // Make Xvfb large enough for any terminal size
131- let xvfbWidth = 1920
132- let xvfbHeight = 1080
130+ // Calculate character cell size based on font
131+ // Approximate: each character cell is ~9px wide, ~18px tall at 14pt font
132+ let charWidth = Math . ceil ( font . size * 0.65 )
133+ let charHeight = Math . ceil ( font . size * 1.3 )
134+
135+ // Track current dimensions (can be resized later)
136+ let currentCols = cols
137+ let currentRows = rows
138+
139+ // Calculate Xvfb size based on terminal dimensions
140+ // Add padding for window decorations and margins
141+ let xvfbWidth = Math . max ( 1920 , cols * charWidth + 100 )
142+ let xvfbHeight = Math . max ( 1080 , rows * charHeight + 100 )
133143
134144 // Start Xvfb
135145 let xvfb = spawn (
136146 'Xvfb' ,
137147 [ `:${ display } ` , '-screen' , '0' , `${ xvfbWidth } x${ xvfbHeight } x24` , '-ac' , '-nolisten' , 'tcp' ] ,
138- { stdio : [ 'ignore' , 'pipe' , 'pipe' ] , detached : true }
148+ { stdio : 'ignore' , detached : true }
139149 )
150+ xvfb . unref ( )
140151
141152 await sleep ( 500 )
142153 await waitForX ( display )
@@ -163,9 +174,10 @@ async function createSession(options) {
163174 LANG : 'en_US.UTF-8' ,
164175 LC_ALL : 'en_US.UTF-8'
165176 } ,
166- stdio : [ 'ignore' , 'pipe' , 'pipe' ] ,
177+ stdio : 'ignore' ,
167178 detached : true
168179 } )
180+ xterm . unref ( )
169181
170182 let windowId = await waitForWindow ( display )
171183
@@ -282,6 +294,53 @@ async function createSession(options) {
282294 throw new Error ( 'waitForText is not supported by the x11 driver' )
283295 } ,
284296
297+ async resize ( newCols , newRows , options = { } ) {
298+ if ( closed ) throw new Error ( 'Session is closed' )
299+ let { clear = true } = options
300+
301+ // Get current window geometry to calculate actual cell size
302+ let { stdout } = await execAsync ( `DISPLAY=:${ display } xdotool getwindowgeometry ${ windowId } ` )
303+ let match = stdout . match ( / G e o m e t r y : ( \d + ) x ( \d + ) / )
304+ if ( ! match ) throw new Error ( 'Could not get window geometry' )
305+
306+ let currentPixelWidth = parseInt ( match [ 1 ] , 10 )
307+ let currentPixelHeight = parseInt ( match [ 2 ] , 10 )
308+
309+ // Calculate actual character cell dimensions from current window
310+ let actualCharWidth = currentPixelWidth / currentCols
311+ let actualCharHeight = currentPixelHeight / currentRows
312+
313+ // Calculate new pixel dimensions
314+ let pixelWidth = Math . round ( newCols * actualCharWidth )
315+ let pixelHeight = Math . round ( newRows * actualCharHeight )
316+
317+ // Resize the Xvfb screen if needed
318+ let newXvfbWidth = Math . max ( xvfbWidth , pixelWidth + 100 )
319+ let newXvfbHeight = Math . max ( xvfbHeight , pixelHeight + 100 )
320+
321+ if ( newXvfbWidth > xvfbWidth || newXvfbHeight > xvfbHeight ) {
322+ // xrandr can resize the virtual screen
323+ await execAsync ( `DISPLAY=:${ display } xrandr --fb ${ newXvfbWidth } x${ newXvfbHeight } ` ) . catch ( ( ) => { } )
324+ xvfbWidth = newXvfbWidth
325+ xvfbHeight = newXvfbHeight
326+ }
327+
328+ // Resize the xterm window
329+ await execAsync ( `DISPLAY=:${ display } xdotool windowsize ${ windowId } ${ pixelWidth } ${ pixelHeight } ` )
330+ await sleep ( 200 )
331+
332+ // Update stored dimensions
333+ currentCols = newCols
334+ currentRows = newRows
335+
336+ // Clear the terminal for a fresh start
337+ if ( clear ) {
338+ await this . type ( 'clear' )
339+ await this . press ( 'Enter' )
340+ await sleep ( 100 )
341+ }
342+ } ,
343+
285344 async screenshot ( ) {
286345 if ( closed ) throw new Error ( 'Session is closed' )
287346
0 commit comments