import React, { useState, useEffect, useRef } from "react"; import Typography from "@mui/material/Typography"; 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 makeStyles from "@mui/styles/makeStyles"; import createStyles from "@mui/styles/createStyles"; import Box from "@mui/material/Box"; import { ITerminal, Output, Link, RawOutput } from "../ITerminal"; import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { TerminalInput } from "./TerminalInput"; import { TerminalEvents, TerminalClearEvents } from "../TerminalEvents"; import { BitFlumeModal } from "../../BitNode/ui/BitFlumeModal"; import { CodingContractModal } from "../../ui/React/CodingContractModal"; import _ from "lodash"; import { ANSIITypography } from "../../ui/React/ANSIITypography"; interface IActionTimerProps { terminal: ITerminal; } function ActionTimer({ terminal }: IActionTimerProps): React.ReactElement { return ( {terminal.getProgressText()} ); } const useStyles = makeStyles((theme: Theme) => createStyles({ nopadding: { padding: theme.spacing(0), }, preformatted: { whiteSpace: "pre-wrap", overflowWrap: "anywhere", margin: theme.spacing(0), }, list: { padding: theme.spacing(0), height: "100%", }, }), ); interface IProps { terminal: ITerminal; router: IRouter; player: IPlayer; } export function TerminalRoot({ terminal, router, player }: IProps): React.ReactElement { const scrollHook = useRef(null); const setRerender = useState(0)[1]; const [key, setKey] = useState(0); function rerender(): void { setRerender((old) => old + 1); } function clear(): void { setKey((key) => key + 1); } useEffect(() => { const debounced = _.debounce(async () => rerender(), 25, { maxWait: 50 }); const unsubscribe = TerminalEvents.subscribe(debounced); return () => { debounced.cancel(); unsubscribe(); }; }, []); useEffect(() => { const debounced = _.debounce(async () => clear(), 25, { maxWait: 50 }); const unsubscribe = TerminalClearEvents.subscribe(debounced); return () => { debounced.cancel(); unsubscribe(); }; }, []); function doScroll(): number | undefined { const hook = scrollHook.current; if (hook !== null) { return window.setTimeout(() => hook.scrollIntoView(true), 50); } } doScroll(); useEffect(() => { let scrollId: number; const id = setTimeout(() => { scrollId = doScroll() ?? 0; }, 50); return () => { clearTimeout(id); clearTimeout(scrollId); }; }, []); const classes = useStyles(); return ( <> {terminal.outputHistory.map((item, i) => ( {item instanceof Output && } {item instanceof RawOutput && ( {item.raw} )} {item instanceof Link && ( <> {item.dashes}>  terminal.connectToServer(player, item.hostname)} > {item.hostname} )} ))} {terminal.action !== null && ( {" "} )}
); }