Make the Web UI console resizable

The instance console was locked to a fixed 300px height. It can now be
drag-resized vertically (native resize handle), and the chosen height is
persisted with useLocalStorage so it is restored on the next visit.

Both the height and the scroll-anchor padding are driven by a
--console-height CSS variable, so the pin-to-bottom behaviour keeps working
at any size.

Closes #938

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
bbassieandClaude Opus 4.8 committed 2026-06-26 14:47:32 +00:00
1 parent a274fe74e1
commit 3fe0c85055
2 files changed
+40 -4

No files matched your search

+31 -1
View File
@@ -4,10 +4,14 @@ import { Select, Typography } from "antd";
import * as lib from "@clusterio/lib";
import { useAccount } from "../model/account";
import useLocalStorage from "../util/useLocalStorage";
import ControlContext from "./ControlContext";
const { Paragraph } = Typography;
const consoleHeightKey = "instance-console-height";
const defaultConsoleHeight = 300;
type Parsed = {
format: string;
type: string;
@@ -99,6 +103,28 @@ export default function LogConsole(props: LogConsoleProps) {
let anchor = useRef<any>();
let [pastLines, setPastLines] = useState([<span key={0}>{"Loading past entries..."}<br/></span>]);
let [lines, setLines] = useState<ReactElement[]>([]);
const [consoleHeight, setConsoleHeight] = useLocalStorage(consoleHeightKey, defaultConsoleHeight);
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 <code> element wrapping the
// anchor.
useEffect(() => {
const element = anchor.current?.parentElement as HTMLElement | undefined;
if (!element || typeof ResizeObserver === "undefined") {
return undefined;
}
const observer = new ResizeObserver(() => {
const next = Math.round(element.offsetHeight);
if (next && next !== consoleHeightRef.current) {
consoleHeightRef.current = next;
setConsoleHeight(next);
}
});
observer.observe(element);
return () => observer.disconnect();
}, []);
useEffect(() => {
let logFilter = {
@@ -163,7 +189,11 @@ export default function LogConsole(props: LogConsoleProps) {
]);
return <>
<Paragraph code className="instance-console">
<Paragraph
code
className="instance-console"
style={{ "--console-height": `${consoleHeight}px` } as React.CSSProperties}
>
{pastLines}
{lines}
<div className="scroll-anchor" key="anchor" ref={anchor} />
+9 -3
View File
@@ -171,13 +171,19 @@ body {
margin: 0;
background: #000;
border-color: #252525;
height: 300px;
/* 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. */
height: var(--console-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. */
padding-top: 300px;
/* 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, 300px);
}
.instance-console .factorio-time {