Skip to content

Commit a625d5d

Browse files
Xterm sizing adjustments (#439)
better size handling for canvas display
1 parent 8ef7958 commit a625d5d

6 files changed

Lines changed: 234 additions & 103 deletions

File tree

bun.lock

Lines changed: 0 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@
9595
"@vercel/otel": "^2.1.2",
9696
"@vercel/speed-insights": "^1.2.0",
9797
"@xterm/addon-canvas": "^0.7.0",
98-
"@xterm/addon-fit": "^0.11.0",
9998
"@xterm/addon-webgl": "^0.19.0",
10099
"@xterm/xterm": "^6.0.0",
101100
"cheerio": "^1.0.0",

src/features/dashboard/terminal/dashboard-terminal.tsx

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

3-
import { type CommandHandle, type Sandbox } from 'e2b'
4-
import { useCallback, useEffect, useRef, useState } from 'react'
3+
import type { CommandHandle, Sandbox } from 'e2b'
4+
import { useCallback, useEffect, useEffectEvent, useRef, useState } from 'react'
55
import type { SandboxManagementAuth } from '@/core/shared/sandbox-management-auth'
66
import {
77
DEFAULT_CWD,
@@ -192,7 +192,7 @@ export default function DashboardTerminal({
192192

193193
const resizePty = useCallback((size: { cols: number; rows: number }) => {
194194
if (sandboxRef.current && pidRef.current) {
195-
void sandboxRef.current.pty.resize(pidRef.current, size)
195+
void sandboxRef.current.pty.resize(pidRef.current, size).catch(() => {})
196196
}
197197
}, [])
198198

@@ -378,7 +378,7 @@ export default function DashboardTerminal({
378378
})
379379
ptyRef.current = pty
380380
pidRef.current = pty.pid
381-
resizeTerminal()
381+
resizeTerminal({ force: true })
382382
setStatus('ready')
383383
appendOutput(`PTY ${pty.pid} attached.\r\n`)
384384
focusTerminal()
@@ -555,38 +555,37 @@ export default function DashboardTerminal({
555555
}
556556
}, [autoStart, launchTarget, queueTerminalCommand, status])
557557

558-
useEffect(() => {
559-
const handlePageHide = (event: PageTransitionEvent) => {
560-
if (event.persisted) return
558+
const handlePageHide = useEffectEvent((event: PageTransitionEvent) => {
559+
if (event.persisted) return
561560

562-
abortCurrentStart()
563-
void closeTerminal()
564-
}
561+
abortCurrentStart()
562+
void closeTerminal()
563+
})
565564

566-
const handlePageShow = (event: PageTransitionEvent) => {
567-
if (!event.persisted || !ptyRef.current) return
565+
const handlePageShow = useEffectEvent((event: PageTransitionEvent) => {
566+
if (!event.persisted || !ptyRef.current) return
568567

569-
resizeTerminal()
570-
focusTerminal()
571-
}
568+
resizeTerminal({ force: true })
569+
focusTerminal()
570+
})
571+
572+
const handleTerminalUnmount = useEffectEvent(() => {
573+
startGenerationRef.current += 1
574+
isStartingRef.current = false
575+
clearPendingInput()
576+
void closeTerminal()
577+
})
572578

579+
useEffect(() => {
573580
window.addEventListener('pagehide', handlePageHide)
574581
window.addEventListener('pageshow', handlePageShow)
575582

576583
return () => {
577584
window.removeEventListener('pagehide', handlePageHide)
578585
window.removeEventListener('pageshow', handlePageShow)
579-
abortCurrentStart()
580-
clearPendingInput()
581-
void closeTerminal()
586+
handleTerminalUnmount()
582587
}
583-
}, [
584-
abortCurrentStart,
585-
clearPendingInput,
586-
closeTerminal,
587-
focusTerminal,
588-
resizeTerminal,
589-
])
588+
}, [])
590589

591590
return (
592591
<>

src/features/dashboard/terminal/terminal-size.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,21 @@ const MAX_CELL_WIDTH_PX = 16
1212
const MIN_CELL_HEIGHT_PX = 8
1313
const MAX_CELL_HEIGHT_PX = 40
1414

15+
type XTermWithRenderDimensions = XTerm & {
16+
_core?: {
17+
_renderService?: {
18+
dimensions?: {
19+
css?: {
20+
cell?: {
21+
width?: number
22+
height?: number
23+
}
24+
}
25+
}
26+
}
27+
}
28+
}
29+
1530
function getElementSize(element: Element | null) {
1631
if (!element) return undefined
1732

@@ -21,18 +36,41 @@ function getElementSize(element: Element | null) {
2136
return rect
2237
}
2338

39+
function getRenderCellSize(terminal: XTerm | null) {
40+
const cell = (terminal as XTermWithRenderDimensions | null)?._core
41+
?._renderService?.dimensions?.css?.cell
42+
43+
if (!cell?.width && !cell?.height) return undefined
44+
45+
return {
46+
width: cell.width,
47+
height: cell.height,
48+
}
49+
}
50+
2451
function getMeasuredCellSize(terminal: XTerm | null) {
52+
const renderCellSize = getRenderCellSize(terminal)
2553
const measureElement = terminal?.element?.querySelector(
2654
'.xterm-char-measure-element'
2755
)
2856
const rowElement = terminal?.element?.querySelector('.xterm-rows > div')
57+
const helperTextArea = terminal?.element?.querySelector(
58+
'.xterm-helper-textarea'
59+
)
2960
const measuredCharSize = getElementSize(measureElement ?? null)
3061
const rowSize = getElementSize(rowElement ?? null)
62+
const helperSize = getElementSize(helperTextArea ?? null)
3163

32-
if (!measuredCharSize && !rowSize) return undefined
64+
if (!renderCellSize && !measuredCharSize && !rowSize && !helperSize) {
65+
return undefined
66+
}
3367

34-
const measuredWidth = measuredCharSize?.width
35-
const measuredHeight = rowSize?.height ?? measuredCharSize?.height
68+
const measuredWidth = renderCellSize?.width ?? measuredCharSize?.width
69+
const measuredHeight =
70+
renderCellSize?.height ??
71+
rowSize?.height ??
72+
measuredCharSize?.height ??
73+
helperSize?.height
3674

3775
return {
3876
width:
@@ -78,9 +116,6 @@ export function calculateTerminalSize(
78116

79117
return {
80118
cols: Math.max(MIN_TERMINAL_COLS, Math.floor(availableWidth / cellWidth)),
81-
rows: Math.max(
82-
MIN_TERMINAL_ROWS,
83-
Math.floor(availableHeight / cellHeight) - 1
84-
),
119+
rows: Math.max(MIN_TERMINAL_ROWS, Math.floor(availableHeight / cellHeight)),
85120
}
86121
}

0 commit comments

Comments
 (0)