| ... | ... | @@ -129,6 +129,7 @@ export default function ThreadTerminalDrawer({ |
| 129 | 129 | startY: number; |
| 130 | 130 | startHeight: number; |
| 131 | 131 | } | null>(null); |
| 132 | const didResizeDuringDragRef = useRef(false); |
| 132 | 133 | |
| 133 | 134 | useEffect(() => { |
| 134 | 135 | onHeightChangeRef.current = onHeightChange; |
| ... | ... | @@ -152,11 +153,17 @@ export default function ThreadTerminalDrawer({ |
| 152 | 153 | [], |
| 153 | 154 | ); |
| 154 | 155 | |
| 155 | | const fitAndResizeTerminal = useCallback(() => { |
| 156 | const fitAndResizeTerminal = useCallback((preserveBottom = false) => { |
| 156 | 157 | const activeTerminal = terminalRef.current; |
| 157 | 158 | const activeFitAddon = fitAddonRef.current; |
| 158 | 159 | if (!activeTerminal || !activeFitAddon) return; |
| 160 | const wasAtBottom = |
| 161 | preserveBottom && |
| 162 | activeTerminal.buffer.active.viewportY >= activeTerminal.buffer.active.baseY; |
| 159 | 163 | activeFitAddon.fit(); |
| 164 | if (wasAtBottom) { |
| 165 | activeTerminal.scrollToBottom(); |
| 166 | } |
| 160 | 167 | void api.terminal |
| 161 | 168 | .resize({ |
| 162 | 169 | threadId, |
| ... | ... | @@ -177,13 +184,14 @@ export default function ThreadTerminalDrawer({ |
| 177 | 184 | if (event.button !== 0) return; |
| 178 | 185 | event.preventDefault(); |
| 179 | 186 | event.currentTarget.setPointerCapture(event.pointerId); |
| 187 | didResizeDuringDragRef.current = false; |
| 180 | 188 | resizeStateRef.current = { |
| 181 | 189 | pointerId: event.pointerId, |
| 182 | 190 | startY: event.clientY, |
| 183 | | startHeight: drawerHeight, |
| 191 | startHeight: drawerHeightRef.current, |
| 184 | 192 | }; |
| 185 | 193 | }, |
| 186 | | [drawerHeight], |
| 194 | [], |
| 187 | 195 | ); |
| 188 | 196 | |
| 189 | 197 | const handleResizePointerMove = useCallback( |
| ... | ... | @@ -194,6 +202,11 @@ export default function ThreadTerminalDrawer({ |
| 194 | 202 | const clampedHeight = clampDrawerHeight( |
| 195 | 203 | resizeState.startHeight + (resizeState.startY - event.clientY), |
| 196 | 204 | ); |
| 205 | if (clampedHeight === drawerHeightRef.current) { |
| 206 | return; |
| 207 | } |
| 208 | didResizeDuringDragRef.current = true; |
| 209 | drawerHeightRef.current = clampedHeight; |
| 197 | 210 | setDrawerHeight(clampedHeight); |
| 198 | 211 | }, |
| 199 | 212 | [], |
| ... | ... | @@ -207,9 +220,13 @@ export default function ThreadTerminalDrawer({ |
| 207 | 220 | if (event.currentTarget.hasPointerCapture(event.pointerId)) { |
| 208 | 221 | event.currentTarget.releasePointerCapture(event.pointerId); |
| 209 | 222 | } |
| 223 | if (!didResizeDuringDragRef.current) { |
| 224 | return; |
| 225 | } |
| 210 | 226 | syncHeight(drawerHeightRef.current); |
| 227 | fitAndResizeTerminal(true); |
| 211 | 228 | }, |
| 212 | | [syncHeight], |
| 229 | [fitAndResizeTerminal, syncHeight], |
| 213 | 230 | ); |
| 214 | 231 | |
| 215 | 232 | useEffect(() => { |
| ... | ... | @@ -219,7 +236,7 @@ export default function ThreadTerminalDrawer({ |
| 219 | 236 | if (changed) { |
| 220 | 237 | setDrawerHeight(clampedHeight); |
| 221 | 238 | } else { |
| 222 | | fitAndResizeTerminal(); |
| 239 | fitAndResizeTerminal(true); |
| 223 | 240 | } |
| 224 | 241 | if (!resizeStateRef.current) { |
| 225 | 242 | syncHeight(clampedHeight); |
| ... | ... | @@ -366,7 +383,9 @@ export default function ThreadTerminalDrawer({ |
| 366 | 383 | } |
| 367 | 384 | }); |
| 368 | 385 | |
| 369 | | const fitTimer = window.setTimeout(fitAndResizeTerminal, 30); |
| 386 | const fitTimer = window.setTimeout(() => { |
| 387 | fitAndResizeTerminal(true); |
| 388 | }, 30); |
| 370 | 389 | void openTerminal(); |
| 371 | 390 | |
| 372 | 391 | return () => { |
| ... | ... | @@ -396,8 +415,12 @@ export default function ThreadTerminalDrawer({ |
| 396 | 415 | const terminal = terminalRef.current; |
| 397 | 416 | const fitAddon = fitAddonRef.current; |
| 398 | 417 | if (!terminal || !fitAddon) return; |
| 418 | const wasAtBottom = terminal.buffer.active.viewportY >= terminal.buffer.active.baseY; |
| 399 | 419 | const frame = window.requestAnimationFrame(() => { |
| 400 | 420 | fitAddon.fit(); |
| 421 | if (wasAtBottom) { |
| 422 | terminal.scrollToBottom(); |
| 423 | } |
| 401 | 424 | void api.terminal |
| 402 | 425 | .resize({ |
| 403 | 426 | threadId, |