From 5cde390c24fb677c77d2b2796dba24a2b6e4dc61 Mon Sep 17 00:00:00 2001 From: bbassie <17990055+bbassie@users.noreply.github.com> Date: Mon, 29 Jun 2026 22:01:06 +0000 Subject: [PATCH] Use a spacer element for console scroll headroom Replace the padding-top scroll headroom with a dedicated .console-spacer element so the pin-to-bottom keeps working without padding inflating the measured height, and trim the style.css comments. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/web_ui/src/components/LogConsole.tsx | 30 +++++++++++-------- packages/web_ui/src/style.css | 21 ++++++------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/packages/web_ui/src/components/LogConsole.tsx b/packages/web_ui/src/components/LogConsole.tsx index b6fad009..c327d4ec 100644 --- a/packages/web_ui/src/components/LogConsole.tsx +++ b/packages/web_ui/src/components/LogConsole.tsx @@ -10,9 +10,7 @@ import ControlContext from "./ControlContext"; const { Paragraph } = Typography; const consoleHeightKey = "instance-console-height"; -// With border-box the height includes the pin-to-bottom padding, so this is the -// full rendered height (matching the previous content-box default of 300 + 300). -const defaultConsoleHeight = 600; +const defaultConsoleHeight = 300; type Parsed = { format: string; @@ -109,12 +107,20 @@ export default function LogConsole(props: LogConsoleProps) { const consoleHeightRef = useRef(consoleHeight); consoleHeightRef.current = consoleHeight; - // Persist the height the user drag-resizes the console to, so it is restored - // on the next visit. The scroll container is the element wrapping the - // anchor. + // The console is resized with the browser's native resize handle, which owns + // the element's inline height. Driving the height from React would rewrite + // that inline style on every re-render and fight the drag (locking the size), + // so the height is only read here (on mount) and persisted (on resize), never + // written back through React. The scroll container is the element + // wrapping the anchor. useEffect(() => { const element = anchor.current?.parentElement as HTMLElement | undefined; - if (!element || typeof ResizeObserver === "undefined") { + if (!element) { + return undefined; + } + element.style.height = `${consoleHeightRef.current}px`; + + if (typeof ResizeObserver === "undefined") { return undefined; } const observer = new ResizeObserver(() => { @@ -191,11 +197,11 @@ export default function LogConsole(props: LogConsoleProps) { ]); return <> - + + {/* Scrollable spacer (one console-height tall) that keeps short output + pinned to the bottom and leaves room to scroll up. Unlike a + padding-top it is scroll content, so it does not constrain resize. */} +
{pastLines} {lines}
diff --git a/packages/web_ui/src/style.css b/packages/web_ui/src/style.css index 6144066b..04348394 100644 --- a/packages/web_ui/src/style.css +++ b/packages/web_ui/src/style.css @@ -166,28 +166,25 @@ body { height: 1px; } +/* Scroll headroom above the output so short output sits at the bottom; it is + scroll content, not box padding, so it does not block dragging smaller. */ +.instance-console code .console-spacer { + height: 100%; +} + .instance-console code { display: block; margin: 0; background: #000; border-color: #252525; - /* Height is driven by --console-height (set from the persisted user choice) - so the console can be drag-resized; the resize handle needs a non-visible - overflow, which overflow-y: scroll provides. border-box keeps padding-top - inside the height so the resize-observer measurement (offsetHeight) does - not include it; otherwise the padding inflates the measured height and the - console can no longer be dragged smaller. */ + /* Drag-resizable (the native handle needs the scroll overflow); height is set + imperatively in LogConsole, border-box keeps padding out of offsetHeight. */ box-sizing: border-box; - height: var(--console-height, 600px); + height: 300px; min-height: 100px; resize: vertical; overflow-y: scroll; white-space: pre-wrap; - - /* Make sure there's at least one pixel of scroll space, this */ - /* is necessary for the view to stick on the scroll anchor. Tracks the */ - /* height so the pin-to-bottom keeps working at any size. */ - padding-top: var(--console-height, 600px); } .instance-console .factorio-time {