diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index fd854eb182e..6f34b34bd8e 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -72,7 +72,7 @@ function App(): React.JSX.Element {
Orca
-
+
{activeView === 'settings' ? : activeWorktreeId ? : }
diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index c5af5caa4f4..87396c067f8 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -330,6 +330,12 @@ position: relative; } +/* Ensure Restty pane root fills its absolutely-positioned container */ +.restty-pane-root { + width: 100% !important; + height: 100% !important; +} + /* ── Landing ─────────────────────────────────────────── */ .landing { diff --git a/src/renderer/src/components/Terminal.tsx b/src/renderer/src/components/Terminal.tsx index 844297ff648..5dbda7c0ce5 100644 --- a/src/renderer/src/components/Terminal.tsx +++ b/src/renderer/src/components/Terminal.tsx @@ -128,7 +128,7 @@ export default function Terminal(): React.JSX.Element | null { }, [activeWorktreeId, handleNewTab, handleCloseTab, setActiveTab]) return ( -
+
{/* Animated tab bar container using CSS grid for smooth height animation */}
{/* Terminal panes container */} -
+
{tabs.map((tab) => ( (null) const resttyRef = useRef(null) - const wasActiveRef = useRef(isActive) + const wasActiveRef = useRef(false) const updateTabTitle = useAppStore((s) => s.updateTabTitle) const updateTabPtyId = useAppStore((s) => s.updateTabPtyId) @@ -162,6 +162,24 @@ export default function TerminalPane({ useEffect(() => { const container = containerRef.current if (!container) return + let resizeRaf: number | null = null + + const queueResizeAll = (focusActive: boolean): void => { + if (resizeRaf !== null) cancelAnimationFrame(resizeRaf) + resizeRaf = requestAnimationFrame(() => { + resizeRaf = null + const restty = resttyRef.current + if (!restty) return + const panes = restty.getPanes() + for (const p of panes) { + p.app.updateSize(true) + } + if (focusActive) { + const active = restty.getActivePane() ?? panes[0] + active?.canvas.focus({ preventScroll: true }) + } + }) + } const onTitleChange = (title: string): void => { updateTabTitle(tabId, title) @@ -214,15 +232,21 @@ export default function TerminalPane({ pane.app.updateSize(true) pane.app.connectPty('') pane.canvas.focus({ preventScroll: true }) + queueResizeAll(true) }, onPaneClosed: () => {}, - onActivePaneChange: () => {} + onActivePaneChange: () => {}, + onLayoutChanged: () => { + queueResizeAll(false) + } }) restty.createInitialPane({ focus: isActive }) resttyRef.current = restty + queueResizeAll(isActive) return () => { + if (resizeRaf !== null) cancelAnimationFrame(resizeRaf) restty.destroy() resttyRef.current = null } @@ -234,8 +258,8 @@ export default function TerminalPane({ const restty = resttyRef.current if (!restty) return - if (isActive && !wasActiveRef.current) { - // Tab just became active - focus and resize + if (isActive) { + // Ensure size/focus is correct both on initial mount and tab activation. requestAnimationFrame(() => { const panes = restty.getPanes() for (const p of panes) { @@ -250,6 +274,23 @@ export default function TerminalPane({ wasActiveRef.current = isActive }, [isActive]) + // ResizeObserver to keep terminal sized to container + useEffect(() => { + const container = containerRef.current + if (!container) return + + const ro = new ResizeObserver(() => { + const restty = resttyRef.current + if (!restty || !isActive) return + const panes = restty.getPanes() + for (const p of panes) { + p.app.updateSize(true) + } + }) + ro.observe(container) + return () => ro.disconnect() + }, [isActive]) + // Terminal pane shortcuts handled at window capture phase so they remain // reliable even when focus is inside the canvas/IME internals. useEffect(() => { @@ -299,8 +340,8 @@ export default function TerminalPane({ return (
) }