From ff0ad7a648450c30b55a8b5d028c5b519811e2ac Mon Sep 17 00:00:00 2001 From: Vassiliy Yegorov Date: Mon, 15 Jun 2026 16:09:39 +0700 Subject: [PATCH] feat(app): stopped panel paints last screen + Resume/Restart fresh controls Co-Authored-By: Claude Sonnet 4.6 --- app/src/LayoutEngine.tsx | 73 ++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/app/src/LayoutEngine.tsx b/app/src/LayoutEngine.tsx index 74b6570..63b62f3 100644 --- a/app/src/LayoutEngine.tsx +++ b/app/src/LayoutEngine.tsx @@ -1,11 +1,12 @@ import { useEffect, useRef, useState } from "react"; -import { Maximize2, Minimize2, RotateCw, GripVertical } from "lucide-react"; +import { Maximize2, Minimize2, RotateCw, GripVertical, Play } from "lucide-react"; +import { Terminal } from "@xterm/xterm"; import { TerminalView } from "./TerminalView"; import { SearchBar } from "./SearchBar"; import { StatusRing } from "./StatusRing"; import { COLORS, FONT, STATE_COLOR } from "./theme"; import type { LayoutNode, SurfaceState, SurfaceView } from "./layoutTypes"; -import { setRatios, restartSurface, setZoom, moveSurface } from "./socketBridge"; +import { setRatios, restartSurface, setZoom, moveSurface, attachSurface } from "./socketBridge"; interface Props { workspaceId: string; @@ -116,6 +117,43 @@ function Node({ node, path, ...rest }: NodeProps) { return ; } +const NERD_FALLBACK_LE = "'Symbols Nerd Font Mono'"; +const fontStackLE = (family: string | null) => + family ? `'${family}', ${NERD_FALLBACK_LE}, monospace` + : `'JetBrains Mono Variable', 'JetBrains Mono', ${NERD_FALLBACK_LE}, monospace`; + +function xtermThemeLE(p: Record) { + return { + background: p["bg-panel"], + foreground: p["text-primary"], + cursor: p["text-primary"], + selectionBackground: p["search-match"], + }; +} + +function StoppedSnapshot({ surfaceId, font, palette }: { surfaceId: string; font: { family: string; size: number } | null; palette: Record | null }) { + const hostRef = useRef(null); + useEffect(() => { + const host = hostRef.current; + if (!host) return; + const term = new Terminal({ + fontFamily: fontStackLE(font?.family ?? null), + fontSize: font?.size ?? 13, + theme: palette ? xtermThemeLE(palette) : undefined, + cursorBlink: false, + disableStdin: true, + scrollback: 0, + }); + term.open(host); + let disposed = false; + void attachSurface(surfaceId, () => {}).then((res) => { + if (!disposed && res.snapshot) term.write(res.snapshot); + }); + return () => { disposed = true; term.dispose(); }; + }, [surfaceId, font, palette]); // eslint-disable-line react-hooks/exhaustive-deps + return
; +} + function Leaf({ id, workspaceId, running, states, surfaces, focusedId, onFocus, zoomed, drop, onStartPanelDrag, searchSurfaceId, searchNonce, onCloseSearch, font, palette }: Omit & { id: string }) { const focused = focusedId === id; const dropEdge = drop && drop.id === id ? drop.edge : null; @@ -142,19 +180,26 @@ function Leaf({ id, workspaceId, running, states, surfaces, focusedId, onFocus, if (running[id] === false) { return card( -
-
Process exited
-
- - {zoomed === id && ( - - )} + + {zoomed === id && ( + + )} +
);