fix: improve Multmux terminal resize handling
- Add ResizeObserver for reliable resize detection - Use requestAnimationFrame for smoother fit operations - Apply full-size styles to xterm elements after fit - Hide tags to maximize terminal area - Fix flex layout for proper container sizing - Add error handling for fit operations during rapid resize 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
5ca4b19aec
commit
abb80c05d8
|
|
@ -249,9 +249,33 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
|
|||
term.loadAddon(fitAddon)
|
||||
term.open(terminalRef.current)
|
||||
|
||||
// Small delay to ensure container is sized
|
||||
// Force xterm elements to fill container completely
|
||||
const applyFullSizeStyles = () => {
|
||||
if (!terminalRef.current) return
|
||||
|
||||
// Get all xterm elements and force them to fill container
|
||||
const xterm = terminalRef.current.querySelector('.xterm') as HTMLElement
|
||||
const xtermScreen = terminalRef.current.querySelector('.xterm-screen') as HTMLElement
|
||||
const xtermViewport = terminalRef.current.querySelector('.xterm-viewport') as HTMLElement
|
||||
|
||||
if (xterm) {
|
||||
xterm.style.width = '100%'
|
||||
xterm.style.height = '100%'
|
||||
}
|
||||
if (xtermScreen) {
|
||||
xtermScreen.style.width = '100%'
|
||||
xtermScreen.style.height = '100%'
|
||||
}
|
||||
if (xtermViewport) {
|
||||
xtermViewport.style.width = '100%'
|
||||
xtermViewport.style.height = '100%'
|
||||
}
|
||||
}
|
||||
|
||||
// Small delay to ensure container is sized, then fit and apply styles
|
||||
setTimeout(() => {
|
||||
fitAddon.fit()
|
||||
applyFullSizeStyles()
|
||||
}, 100)
|
||||
|
||||
xtermRef.current = term
|
||||
|
|
@ -266,13 +290,62 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
|
|||
|
||||
// Fit terminal when shape resizes
|
||||
useEffect(() => {
|
||||
if (fitAddonRef.current && xtermRef.current) {
|
||||
setTimeout(() => {
|
||||
fitAddonRef.current?.fit()
|
||||
}, 50)
|
||||
if (fitAddonRef.current && xtermRef.current && terminalRef.current) {
|
||||
// Use requestAnimationFrame to ensure DOM has updated
|
||||
requestAnimationFrame(() => {
|
||||
// Double-check the terminal container exists and has dimensions
|
||||
if (terminalRef.current && terminalRef.current.clientWidth > 0 && terminalRef.current.clientHeight > 0) {
|
||||
try {
|
||||
fitAddonRef.current?.fit()
|
||||
} catch (e) {
|
||||
console.warn('Failed to fit terminal:', e)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, [shape.props.w, shape.props.h, isMinimized])
|
||||
|
||||
// Also add a ResizeObserver for more reliable resize detection
|
||||
useEffect(() => {
|
||||
if (!terminalRef.current || !fitAddonRef.current) return
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
if (fitAddonRef.current && xtermRef.current && terminalRef.current) {
|
||||
requestAnimationFrame(() => {
|
||||
try {
|
||||
fitAddonRef.current?.fit()
|
||||
|
||||
// Reapply full-size styles after fit
|
||||
const xterm = terminalRef.current?.querySelector('.xterm') as HTMLElement
|
||||
const xtermScreen = terminalRef.current?.querySelector('.xterm-screen') as HTMLElement
|
||||
const xtermViewport = terminalRef.current?.querySelector('.xterm-viewport') as HTMLElement
|
||||
|
||||
if (xterm) {
|
||||
xterm.style.width = '100%'
|
||||
xterm.style.height = '100%'
|
||||
}
|
||||
if (xtermScreen) {
|
||||
xtermScreen.style.width = '100%'
|
||||
xtermScreen.style.height = '100%'
|
||||
}
|
||||
if (xtermViewport) {
|
||||
xtermViewport.style.width = '100%'
|
||||
xtermViewport.style.height = '100%'
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore fit errors during rapid resizing
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
resizeObserver.observe(terminalRef.current)
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect()
|
||||
}
|
||||
}, [shape.props.token]) // Re-create observer when terminal is initialized
|
||||
|
||||
// WebSocket connection
|
||||
useEffect(() => {
|
||||
if (!shape.props.token || !shape.props.serverUrl) {
|
||||
|
|
@ -649,19 +722,14 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
|
|||
shapeId={shape.id}
|
||||
isPinnedToView={shape.props.pinnedToView}
|
||||
onPinToggle={handlePinToggle}
|
||||
tags={shape.props.tags}
|
||||
onTagsChange={(newTags) => {
|
||||
this.editor.updateShape<IMultmuxShape>({
|
||||
id: shape.id,
|
||||
type: 'Multmux',
|
||||
props: { ...shape.props, tags: newTags }
|
||||
})
|
||||
}}
|
||||
tagsEditable={true}
|
||||
tags={[]} // Hide tags for terminal to maximize terminal area
|
||||
tagsEditable={false}
|
||||
>
|
||||
<div style={{
|
||||
flex: '1 1 0',
|
||||
minHeight: 0,
|
||||
minWidth: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#1e1e2e',
|
||||
color: '#cdd6f4',
|
||||
fontFamily: 'monospace',
|
||||
|
|
@ -669,6 +737,8 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
|
|||
pointerEvents: 'all',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
boxSizing: 'border-box',
|
||||
}}>
|
||||
{/* Status bar */}
|
||||
<div style={{
|
||||
|
|
@ -678,6 +748,7 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
|
|||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
flexShrink: 0, // Don't shrink the status bar
|
||||
}}>
|
||||
<span>
|
||||
{connected ? '🟢 Connected' : '🔴 Disconnected'}
|
||||
|
|
@ -691,9 +762,13 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
|
|||
<div
|
||||
ref={terminalRef}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '4px',
|
||||
flex: '1 1 0',
|
||||
minHeight: 0,
|
||||
minWidth: 0,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
boxSizing: 'border-box',
|
||||
backgroundColor: '#1e1e2e', // Match terminal background to hide gaps
|
||||
}}
|
||||
onPointerDown={(e) => {
|
||||
// Allow pointer events for text selection but stop propagation to prevent tldraw interactions
|
||||
|
|
|
|||
Loading…
Reference in New Issue