TERMINAL: Fix inconsistent / janky scrolling behavior (#1063)

This commit is contained in:
Snarling 2024-01-31 19:32:42 -05:00 committed by GitHub
parent cf45981cd2
commit 0ded11af53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 41 additions and 54 deletions

@ -12,9 +12,6 @@ import { longestCommonStart } from "../../utils/StringHelperFunctions";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
textfield: {
margin: theme.spacing(0),
},
input: { input: {
backgroundColor: theme.colors.backgroundprimary, backgroundColor: theme.colors.backgroundprimary,
}, },
@ -24,14 +21,10 @@ const useStyles = makeStyles((theme: Theme) =>
preformatted: { preformatted: {
margin: theme.spacing(0), margin: theme.spacing(0),
}, },
list: {
padding: theme.spacing(0),
height: "100%",
},
absolute: { absolute: {
margin: theme.spacing(0), margin: theme.spacing(0),
position: "absolute", position: "absolute",
bottom: "5px", bottom: "12px",
opacity: "0.75", opacity: "0.75",
maxWidth: "100%", maxWidth: "100%",
whiteSpace: "pre", whiteSpace: "pre",
@ -419,7 +412,7 @@ export function TerminalInput(): React.ReactElement {
disabled={Terminal.action !== null} disabled={Terminal.action !== null}
autoComplete="off" autoComplete="off"
value={value} value={value}
classes={{ root: classes.textfield }} classes={{ root: classes.preformatted }}
onChange={handleValueChange} onChange={handleValueChange}
inputRef={terminalInput} inputRef={terminalInput}
InputProps={{ InputProps={{

@ -1,12 +1,8 @@
import React, { useState, useEffect, useRef } from "react"; import React, { useState, useEffect, useRef } from "react";
import Typography from "@mui/material/Typography"; import { Link as MuiLink, Typography } from "@mui/material";
import List from "@mui/material/List";
import ListItem from "@mui/material/ListItem";
import { Link as MuiLink } from "@mui/material";
import { Theme } from "@mui/material/styles"; import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles"; import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles"; import createStyles from "@mui/styles/createStyles";
import Box from "@mui/material/Box";
import _ from "lodash"; import _ from "lodash";
import { Output, Link, RawOutput } from "../OutputTypes"; import { Output, Link, RawOutput } from "../OutputTypes";
@ -22,8 +18,16 @@ import { TerminalActionTimer } from "./TerminalActionTimer";
const useStyles = makeStyles((theme: Theme) => const useStyles = makeStyles((theme: Theme) =>
createStyles({ createStyles({
nopadding: { container: {
padding: theme.spacing(0), display: "flex",
flexDirection: "column",
height: "calc(100vh - 16px)",
},
entries: {
padding: 0,
overflow: "scroll",
flex: "0 1 auto",
margin: "auto 0 0",
}, },
preformatted: { preformatted: {
whiteSpace: "pre-wrap", whiteSpace: "pre-wrap",
@ -31,16 +35,11 @@ const useStyles = makeStyles((theme: Theme) =>
margin: theme.spacing(0), margin: theme.spacing(0),
width: "100%", width: "100%",
}, },
list: {
padding: theme.spacing(0),
height: "100%",
width: "100%",
},
}), }),
); );
export function TerminalRoot(): React.ReactElement { export function TerminalRoot(): React.ReactElement {
const scrollHook = useRef<HTMLDivElement>(null); const scrollHook = useRef<HTMLUListElement>(null);
const rerender = useRerender(); const rerender = useRerender();
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
@ -66,7 +65,7 @@ export function TerminalRoot(): React.ReactElement {
function doScroll(): number | undefined { function doScroll(): number | undefined {
const hook = scrollHook.current; const hook = scrollHook.current;
if (hook !== null) { if (hook !== null) {
return window.setTimeout(() => hook.scrollIntoView(true), 50); return window.setTimeout(() => (hook.scrollTop = hook.scrollHeight), 50);
} }
} }
@ -85,39 +84,34 @@ export function TerminalRoot(): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
return ( return (
<> <div className={classes.container}>
<Box width="100%" minHeight="100vh" display={"flex"} alignItems={"flex-end"}> <ul key={key} id="terminal" className={classes.entries} ref={scrollHook}>
<List key={key} id="terminal" classes={{ root: classes.list }}> {Terminal.outputHistory.map((item, i) => (
{Terminal.outputHistory.map((item, i) => ( <li key={i}>
<ListItem key={i} classes={{ root: classes.nopadding }}> {item instanceof Output && <ANSIITypography text={item.text} color={item.color} />}
{item instanceof Output && <ANSIITypography text={item.text} color={item.color} />} {item instanceof RawOutput && (
{item instanceof RawOutput && ( <Typography classes={{ root: classes.preformatted }} paragraph={false}>
<Typography classes={{ root: classes.preformatted }} paragraph={false}> {item.raw}
{item.raw} </Typography>
</Typography> )}
)} {item instanceof Link && (
{item instanceof Link && ( <Typography classes={{ root: classes.preformatted }}>
<Typography classes={{ root: classes.preformatted }}> {item.dashes}
{item.dashes} <MuiLink onClick={() => Terminal.connectToServer(item.hostname)}>{item.hostname}</MuiLink>
<MuiLink onClick={() => Terminal.connectToServer(item.hostname)}>{item.hostname}</MuiLink> </Typography>
</Typography> )}
)} </li>
</ListItem> ))}
))}
{Terminal.action !== null && ( {Terminal.action !== null && (
<ListItem classes={{ root: classes.nopadding }}> <li>
<TerminalActionTimer />{" "} <TerminalActionTimer />{" "}
</ListItem> </li>
)} )}
</List> </ul>
<div ref={scrollHook}></div> <TerminalInput />
</Box>
<Box position="sticky" bottom={0} width="100%" px={0}>
<TerminalInput />
</Box>
<BitFlumeModal /> <BitFlumeModal />
<CodingContractModal /> <CodingContractModal />
</> </div>
); );
} }