diff --git a/package-lock.json b/package-lock.json index 2c4ccd75a..4a2da690d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bitburner", - "version": "1.1.0", + "version": "1.2.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bitburner", - "version": "1.1.0", + "version": "1.2.0", "hasInstallScript": true, "license": "SEE LICENSE IN license.txt", "dependencies": { @@ -22,6 +22,7 @@ "@types/escodegen": "^0.0.7", "@types/numeral": "0.0.25", "@types/react": "^17.0.21", + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-dom": "^17.0.9", "@types/react-resizable": "^1.7.3", "acorn": "^8.4.1", @@ -41,6 +42,7 @@ "numeral": "2.0.6", "raw-loader": "^4.0.2", "react": "^17.0.2", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", "react-draggable": "^4.4.4", "react-resizable": "^3.0.4", @@ -3984,6 +3986,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -4062,6 +4073,14 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "17.0.9", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz", @@ -4078,6 +4097,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz", + "integrity": "sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/react-resizable": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/@types/react-resizable/-/react-resizable-1.7.4.tgz", @@ -6659,6 +6689,14 @@ "node": "*" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, "node_modules/css-select": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", @@ -14566,6 +14604,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -16303,6 +16346,11 @@ } ] }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/ramda": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", @@ -16429,6 +16477,24 @@ "node": ">=0.10.0" } }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz", + "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0", + "react-dom": "^16.8.5 || ^17.0.0" + } + }, "node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -16460,6 +16526,30 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz", @@ -16625,6 +16715,14 @@ "node": ">=8.10.0" } }, + "node_modules/redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -19143,6 +19241,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "node_modules/tiny-invariant": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", + "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" + }, "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -19717,6 +19820,14 @@ "node": ">=0.10.0" } }, + "node_modules/use-memo-one": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0" + } + }, "node_modules/util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", @@ -24597,6 +24708,15 @@ "@types/node": "*" } }, + "@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -24675,6 +24795,14 @@ "csstype": "^3.0.2" } }, + "@types/react-beautiful-dnd": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.2.tgz", + "integrity": "sha512-+OvPkB8CdE/bGdXKyIhc/Lm2U7UAYCCJgsqmopFmh9gbAudmslkI8eOrPDjg4JhwSE6wytz4a3/wRjKtovHVJg==", + "requires": { + "@types/react": "*" + } + }, "@types/react-dom": { "version": "17.0.9", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.9.tgz", @@ -24691,6 +24819,17 @@ "@types/react": "*" } }, + "@types/react-redux": { + "version": "7.1.20", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz", + "integrity": "sha512-q42es4c8iIeTgcnB+yJgRTTzftv3eYYvCZOh1Ckn2eX/3o5TdsQYKUWpLoLuGlcY/p+VAhV9IOEZJcWk/vfkXw==", + "requires": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "@types/react-resizable": { "version": "1.7.4", "resolved": "https://registry.npmjs.org/@types/react-resizable/-/react-resizable-1.7.4.tgz", @@ -26762,6 +26901,14 @@ "randomfill": "^1.0.3" } }, + "css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "requires": { + "tiny-invariant": "^1.0.6" + } + }, "css-select": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.1.3.tgz", @@ -32913,6 +33060,11 @@ "fs-monkey": "1.0.3" } }, + "memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -34288,6 +34440,11 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, + "raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "ramda": { "version": "0.27.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", @@ -34384,6 +34541,20 @@ "object-assign": "^4.1.1" } }, + "react-beautiful-dnd": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz", + "integrity": "sha512-aGvblPZTJowOWUNiwd6tNfEpgkX5OxmpqxHKNW/4VmvZTNTbeiq7bA3bn5T+QSF2uibXB0D1DmJsb1aC/+3cUA==", + "requires": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + } + }, "react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", @@ -34408,6 +34579,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "react-redux": { + "version": "7.2.6", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.6.tgz", + "integrity": "sha512-10RPdsz0UUrRL1NZE0ejTkucnclYSgXp5q+tB5SWx2qeG2ZJQJyymgAhwKy73yiL/13btfB6fPr+rgbMAaZIAQ==", + "requires": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + } + }, "react-refresh": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz", @@ -34537,6 +34721,14 @@ "picomatch": "^2.2.1" } }, + "redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -36569,6 +36761,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tiny-invariant": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", + "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" + }, "tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", @@ -37025,6 +37222,12 @@ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, + "use-memo-one": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.2.tgz", + "integrity": "sha512-u2qFKtxLsia/r8qG0ZKkbytbztzRb317XCkT7yP8wxL0tZ/CzK2G+WWie5vWvpyeP7+YoPIwbJoIHJ4Ba4k0oQ==", + "requires": {} + }, "util": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", diff --git a/package.json b/package.json index ab76e5851..947a2d187 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/escodegen": "^0.0.7", "@types/numeral": "0.0.25", "@types/react": "^17.0.21", + "@types/react-beautiful-dnd": "^13.1.2", "@types/react-dom": "^17.0.9", "@types/react-resizable": "^1.7.3", "acorn": "^8.4.1", @@ -41,6 +42,7 @@ "numeral": "2.0.6", "raw-loader": "^4.0.2", "react": "^17.0.2", + "react-beautiful-dnd": "^13.1.0", "react-dom": "^17.0.2", "react-draggable": "^4.4.4", "react-resizable": "^3.0.4", diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index 021e74eaa..b46abf5f4 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -15,7 +15,7 @@ import { TextFile } from "../../TextFile"; import { calculateRamUsage, checkInfiniteLoop } from "../../Script/RamCalculations"; import { RamCalculationErrorCode } from "../../Script/RamCalculationErrorCodes"; import { numeralWrapper } from "../../ui/numeralFormat"; -import { CursorPositions } from "../CursorPositions"; +import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'; import { NetscriptFunctions } from "../../NetscriptFunctions"; import { WorkerScript } from "../../Netscript/WorkerScript"; @@ -27,20 +27,31 @@ import { loadThemes } from "./themes"; import { GetServer } from "../../Server/AllServers"; import Button from "@mui/material/Button"; -import Tooltip from "@mui/material/Tooltip"; import Typography from "@mui/material/Typography"; import Link from "@mui/material/Link"; import Box from "@mui/material/Box"; import IconButton from "@mui/material/IconButton"; import SettingsIcon from "@mui/icons-material/Settings"; +import { PromptEvent } from "../../ui/React/PromptManager"; import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts"; + +interface IProps { + filename: string; + code: string; + hostname: string; + player: IPlayer; + router: IRouter; +} + +// TODO: try to removve global symbols let symbolsLoaded = false; let symbols: string[] = []; export function SetupTextEditor(): void { const ns = NetscriptFunctions({} as WorkerScript); + // Populates symbols for text editor function populate(ns: any): string[] { let symbols: string[] = []; const keys = Object.keys(ns); @@ -53,36 +64,19 @@ export function SetupTextEditor(): void { symbols.push(key); } } + return symbols; } + symbols = populate(ns); const exclude = ["heart", "break", "exploit", "bypass", "corporation", "alterReality"]; symbols = symbols.filter((symbol: string) => !exclude.includes(symbol)).sort(); } -interface IProps { - filename: string; - code: string; - hostname: string; - player: IPlayer; - router: IRouter; -} - -/* - -*/ - -// How to load function definition in monaco -// https://github.com/Microsoft/monaco-editor/issues/1415 -// https://microsoft.github.io/monaco-editor/api/modules/monaco.languages.html -// https://www.npmjs.com/package/@monaco-editor/react#development-playground -// https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages -// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39 -// https://blog.checklyhq.com/customizing-monaco/ // Holds all the data for a open script -class openScript { +class OpenScript { fileName: string; code: string; hostname: string; @@ -98,17 +92,23 @@ class openScript { } } -const openScripts = new Array(); // Holds all open scripts -let currentScript = {} as openScript; // Script currently being viewed - +// Called every time script editor is opened export function Root(props: IProps): React.ReactElement { const editorRef = useRef(null); const monacoRef = useRef(null); - const [filename] = useState(props.filename); - const [code] = useState(props.code); - const [decorations, setDecorations] = useState([]); + + const [openScripts, setOpenScripts] = useState( + window.localStorage.getItem('scriptEditorOpenScripts') !== null ? JSON.parse(window.localStorage.getItem('scriptEditorOpenScripts')!) : [] + ); + + const [currentScript, setCurrentScript] = useState( + window.localStorage.getItem('scriptEditorCurrentScript') !== null ? JSON.parse(window.localStorage.getItem('scriptEditorCurrentScript')!) : null + ); + const [ram, setRAM] = useState("RAM: ???"); const [updatingRam, setUpdatingRam] = useState(false); + const [decorations, setDecorations] = useState([]); + const [optionsOpen, setOptionsOpen] = useState(false); const [options, setOptions] = useState({ theme: Settings.MonacoTheme, @@ -116,6 +116,53 @@ export function Root(props: IProps): React.ReactElement { fontSize: Settings.MonacoFontSize, }); + useEffect(() => { + // Save currentScript + window.localStorage.setItem('scriptEditorCurrentScript', JSON.stringify(currentScript, (key, value) => { + if (key == 'model') return undefined; + return value; + })); + + // Save openScripts + window.localStorage.setItem('scriptEditorOpenScripts', JSON.stringify(openScripts, (key, value) => { + if (key == 'model') return undefined; + return value; + })) + }, [currentScript, openScripts]) + + useEffect(() => { + if (currentScript !== null) { + updateRAM(currentScript.code); + } + }, []); + + useEffect(() => { + function maybeSave(event: KeyboardEvent): void { + if (Settings.DisableHotkeys) return; + //Ctrl + b + if (event.keyCode == 66 && (event.ctrlKey || event.metaKey)) { + event.preventDefault(); + save(); + } + + // CTRL/CMD + S + if (event.code == `KeyS` && (event.ctrlKey || event.metaKey)) { + event.preventDefault(); + event.stopPropagation(); + save(); + } + } + document.addEventListener("keydown", maybeSave); + return () => document.removeEventListener("keydown", maybeSave); + }); + + // Generates a new model for the script + function regenerateModel(script: OpenScript): void { + if (monacoRef.current !== null) { + script.model = monacoRef.current.editor.createModel(script.code, "javascript"); + } + } + const debouncedSetRAM = useMemo( () => debounce((s) => { @@ -125,7 +172,225 @@ export function Root(props: IProps): React.ReactElement { [], ); + async function updateRAM(newCode: string): Promise { + setUpdatingRam(true); + const codeCopy = newCode + ""; + const ramUsage = await calculateRamUsage(codeCopy, props.player.getCurrentServer().scripts); + if (ramUsage > 0) { + debouncedSetRAM("RAM: " + numeralWrapper.formatRAM(ramUsage)); + return; + } + switch (ramUsage) { + case RamCalculationErrorCode.ImportError: { + debouncedSetRAM("RAM: Import Error"); + break; + } + case RamCalculationErrorCode.URLImportError: { + debouncedSetRAM("RAM: HTTP Import Error"); + break; + } + case RamCalculationErrorCode.SyntaxError: + default: { + debouncedSetRAM("RAM: Syntax Error"); + break; + } + } + return new Promise(() => undefined); + } + + // Formats the code + function beautify(): void { + if (editorRef.current === null) return; + editorRef.current.getAction("editor.action.formatDocument").run(); + } + + // How to load function definition in monaco + // https://github.com/Microsoft/monaco-editor/issues/1415 + // https://microsoft.github.io/monaco-editor/api/modules/monaco.languages.html + // https://www.npmjs.com/package/@monaco-editor/react#development-playground + // https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-custom-languages + // https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39 + // https://blog.checklyhq.com/customizing-monaco/ + // Before the editor is mounted + function beforeMount(monaco: any): void { + if (symbolsLoaded) return; + // Setup monaco auto completion + symbolsLoaded = true; + monaco.languages.registerCompletionItemProvider("javascript", { + provideCompletionItems: () => { + const suggestions = []; + for (const symbol of symbols) { + suggestions.push({ + label: symbol, + kind: monaco.languages.CompletionItemKind.Function, + insertText: symbol, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + }); + } + return { suggestions: suggestions }; + }, + }); + + (async function () { + // We have to improve the default js language otherwise theme sucks + const l = await monaco.languages + .getLanguages() + .find((l: any) => l.id === "javascript") + .loader(); + l.language.tokenizer.root.unshift(["ns", { token: "ns" }]); + for (const symbol of symbols) l.language.tokenizer.root.unshift([symbol, { token: "netscriptfunction" }]); + const otherKeywords = ["let", "const", "var", "function"]; + const otherKeyvars = ["true", "false", "null", "undefined"]; + otherKeywords.forEach((k) => l.language.tokenizer.root.unshift([k, { token: "otherkeywords" }])); + otherKeyvars.forEach((k) => l.language.tokenizer.root.unshift([k, { token: "otherkeyvars" }])); + l.language.tokenizer.root.unshift(["this", { token: "this" }]); + })(); + + const source = (libSource + "").replace(/export /g, ""); + monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts"); + monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); + loadThemes(monaco); + } + + + // When the editor is mounted + function onMount(editor: IStandaloneCodeEditor, monaco: Monaco) { + editorRef.current = editor; + monacoRef.current = monaco; + + if (editorRef.current === null || monacoRef.current === null) return; + + if (props.filename) { + // Check if file is already opened + let openScriptIndex = openScripts.findIndex(script => script.fileName === props.filename && script.hostname === props.hostname); + if (openScriptIndex !== -1) { + // Script is already opened + if (openScripts[openScriptIndex].model === undefined || openScripts[openScriptIndex].model === null || openScripts[openScriptIndex].model.isDisposed()) { + regenerateModel(openScripts[openScriptIndex]); + } + + setCurrentScript(openScripts[openScriptIndex]); + editorRef.current.setModel(openScripts[openScriptIndex].model); + editorRef.current.setPosition(openScripts[openScriptIndex].lastPosition); + editorRef.current.revealLineInCenter(openScripts[openScriptIndex].lastPosition.lineNumber); + updateRAM(openScripts[openScriptIndex].code); + } else { + // Open script + var newScript = new OpenScript(props.filename, props.code, props.hostname, new monacoRef.current.Position(0, 0), monacoRef.current.editor.createModel(props.code, 'javascript')); + setOpenScripts(oldArray => [...oldArray, newScript]); + setCurrentScript({ ...newScript }); + editorRef.current.setModel(newScript.model); + updateRAM(newScript.code); + } + } else if (currentScript !== null) { + // Open currentscript + regenerateModel(currentScript); + editorRef.current.setModel(currentScript.model); + editorRef.current.setPosition(currentScript.lastPosition); + editorRef.current.revealLineInCenter(currentScript.lastPosition.lineNumber); + updateRAM(currentScript.code); + } + } + + function infLoop(newCode: string): void { + if (editorRef.current === null || currentScript === null) return; + if (!currentScript.fileName.endsWith(".ns") && !currentScript.fileName.endsWith(".js")) return; + const awaitWarning = checkInfiniteLoop(newCode); + if (awaitWarning !== -1) { + const newDecorations = editorRef.current.deltaDecorations(decorations, [ + { + range: { + startLineNumber: awaitWarning, + startColumn: 1, + endLineNumber: awaitWarning, + endColumn: 10, + }, + options: { + isWholeLine: true, + glyphMarginClassName: "myGlyphMarginClass", + glyphMarginHoverMessage: { + value: "Possible infinite loop, await something.", + }, + }, + }, + ]); + setDecorations(newDecorations); + } else { + const newDecorations = editorRef.current.deltaDecorations(decorations, []); + setDecorations(newDecorations); + } + } + + // When the code is updated within the editor + function updateCode(newCode?: string) { + if (newCode === undefined) return; + updateRAM(newCode); + if (editorRef.current !== null) { + var newPos = editorRef.current.getPosition(); + if (newPos === null) return; + setCurrentScript(oldScript => ({ ...oldScript!, code: newCode, lastPosition: newPos! })) + if (currentScript !== null) { + let curIndex = openScripts.findIndex(script => script.fileName === currentScript.fileName && script.hostname === currentScript.hostname); + let newArr = [...openScripts]; + let tempScript = currentScript; + tempScript.code = newCode; + newArr[curIndex] = tempScript; + setOpenScripts([...newArr]); + } + try { + infLoop(newCode); + } catch (err) { } + } + } + + function saveScript(scriptToSave: OpenScript): void { + const server = GetServer(scriptToSave.hostname); + if (server === null) throw new Error("Server should not be null but it is."); + if (isScriptFilename(scriptToSave.fileName)) { + //If the current script already exists on the server, overwrite it + for (let i = 0; i < server.scripts.length; i++) { + if (scriptToSave.fileName == server.scripts[i].filename) { + server.scripts[i].saveScript( + scriptToSave.fileName, + scriptToSave.code, + props.player.currentServer, + server.scripts, + ); + if (Settings.SaveGameOnFileSave) saveObject.saveGame(); + props.router.toTerminal(); + return; + } + } + + //If the current script does NOT exist, create a new one + const script = new Script(); + script.saveScript(scriptToSave.fileName, scriptToSave.code, props.player.currentServer, server.scripts); + server.scripts.push(script); + } else if (scriptToSave.fileName.endsWith(".txt")) { + for (let i = 0; i < server.textFiles.length; ++i) { + if (server.textFiles[i].fn === scriptToSave.fileName) { + server.textFiles[i].write(scriptToSave.code); + if (Settings.SaveGameOnFileSave) saveObject.saveGame(); + props.router.toTerminal(); + return; + } + } + const textFile = new TextFile(scriptToSave.fileName, scriptToSave.code); + server.textFiles.push(textFile); + } else { + dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)"); + return; + } + + if (Settings.SaveGameOnFileSave) saveObject.saveGame(); + props.router.toTerminal(); + } + function save(): void { + if (currentScript === null) { + console.log("currentScript is null when it shouldn't be. Unabel to save script"); + return; + } // this is duplicate code with saving later. if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) { //Make sure filename + code properly follow tutorial @@ -172,6 +437,7 @@ export function Root(props: IProps): React.ReactElement { server.scripts, ); if (Settings.SaveGameOnFileSave) saveObject.saveGame(); + props.router.toTerminal(); return; } } @@ -185,6 +451,7 @@ export function Root(props: IProps): React.ReactElement { if (server.textFiles[i].fn === currentScript.fileName) { server.textFiles[i].write(currentScript.code); if (Settings.SaveGameOnFileSave) saveObject.saveGame(); + props.router.toTerminal(); return; } } @@ -196,407 +463,97 @@ export function Root(props: IProps): React.ReactElement { } if (Settings.SaveGameOnFileSave) saveObject.saveGame(); + props.router.toTerminal(); } - function beautify(): void { - if (editorRef.current === null) return; - editorRef.current.getAction("editor.action.formatDocument").run(); + function reorder(list: Array, startIndex: number, endIndex: number) { + const result = Array.from(list); + const [removed] = result.splice(startIndex, 1); + result.splice(endIndex, 0, removed); + + return result; } - function infLoop(newCode: string): void { - if (editorRef.current === null) return; - if (!currentScript.fileName.endsWith(".ns") && !currentScript.fileName.endsWith(".js")) return; - const awaitWarning = checkInfiniteLoop(newCode); - if (awaitWarning !== -1) { - const newDecorations = editorRef.current.deltaDecorations(decorations, [ - { - range: { - startLineNumber: awaitWarning, - startColumn: 1, - endLineNumber: awaitWarning, - endColumn: 10, - }, - options: { - isWholeLine: true, - glyphMarginClassName: "myGlyphMarginClass", - glyphMarginHoverMessage: { - value: "Possible infinite loop, await something.", - }, - }, - }, - ]); - setDecorations(newDecorations); - } else { - const newDecorations = editorRef.current.deltaDecorations(decorations, []); - setDecorations(newDecorations); - } - } - - function updateCode(newCode?: string): void { - if (newCode === undefined) return; - updateRAM(newCode); - currentScript.code = newCode; - try { - if (editorRef.current !== null) { - infLoop(newCode); - } - } catch (err) {} - } - - // calculate it once the first time the file is loaded. - useEffect(() => { - updateRAM(currentScript.code); - }, []); - - async function updateRAM(newCode: string): Promise { - setUpdatingRam(true); - const codeCopy = newCode + ""; - const ramUsage = await calculateRamUsage(codeCopy, props.player.getCurrentServer().scripts); - if (ramUsage > 0) { - debouncedSetRAM("RAM: " + numeralWrapper.formatRAM(ramUsage)); + function onDragEnd(result: any) { + // Dropped outside of the list + if (!result.destination) { + result return; } - switch (ramUsage) { - case RamCalculationErrorCode.ImportError: { - debouncedSetRAM("RAM: Import Error"); - break; - } - case RamCalculationErrorCode.URLImportError: { - debouncedSetRAM("RAM: HTTP Import Error"); - break; - } - case RamCalculationErrorCode.SyntaxError: - default: { - debouncedSetRAM("RAM: Syntax Error"); - break; - } - } - return new Promise(() => undefined); + + const items = reorder(openScripts, result.source.index, result.destination.index); + + setOpenScripts(items); } - useEffect(() => { - function maybeSave(event: KeyboardEvent): void { - if (Settings.DisableHotkeys) return; - - // CTRL/CMD + S - if (event.code == `KeyS` && (event.ctrlKey || event.metaKey)) { - event.preventDefault(); - event.stopPropagation(); - save(); - } + function onTabClick(index: number) { + if (currentScript !== null) { + // Save currentScript to openScripts + let curIndex = openScripts.findIndex(script => script.fileName === currentScript.fileName && script.hostname === currentScript.hostname); + openScripts[curIndex] = currentScript; } - document.addEventListener("keydown", maybeSave); - return () => document.removeEventListener("keydown", maybeSave); - }); - // Generates a new model for the script - function regenerateModel(script: openScript): void { - if (monacoRef.current !== null) { - script.model = monacoRef.current.editor.createModel(script.code, "javascript"); + setCurrentScript({ ...openScripts[index] }); + + if (editorRef.current !== null && openScripts[index] !== null) { + if (openScripts[index].model === undefined || openScripts[index].model.isDisposed()) { + regenerateModel(openScripts[index]); + } + editorRef.current.setModel(openScripts[index].model); + + editorRef.current.setPosition(openScripts[index].lastPosition); + editorRef.current.revealLineInCenter(openScripts[index].lastPosition.lineNumber); + updateRAM(openScripts[index].code); } } - // Sets the currently viewed script - function setCurrentScript(script: openScript): void { - // Update last position - if (editorRef.current !== null) { - if (currentScript !== null) { - const currentPosition = editorRef.current.getPosition(); - if (currentPosition !== null) { - currentScript.lastPosition = currentPosition; - } - } - - editorRef.current.setModel(script.model); - currentScript = script; - editorRef.current.setPosition(currentScript.lastPosition); - editorRef.current.revealLine(currentScript.lastPosition.lineNumber); - updateRAM(currentScript.code); - } - } - - // Gets a currently opened script - function getOpenedScript(fileName: string, hostname: string): openScript | null { - for (const script of openScripts) { - if (script.fileName === fileName && script.hostname === hostname) { - return script; - } + async function onTabClose(index: number) { + // See if the script on the server is up to date + let closingScript = openScripts[index]; + let savedOpenScripts: Array = JSON.parse(window.localStorage.getItem('scriptEditorOpenScripts')!); + let savedScriptIndex = savedOpenScripts.findIndex(script => script.fileName === closingScript.fileName && script.hostname === closingScript.hostname); + let savedScriptCode = ''; + if (savedScriptIndex !== -1) { + savedScriptCode = savedOpenScripts[savedScriptIndex].code; } - return null; - } - - function saveScript(script: openScript): void { - const server = GetServer(script.hostname); - if (server === null) throw new Error("Server should not be null but it is."); - let found = false; - for (let i = 0; i < server.scripts.length; i++) { - if (script.fileName == server.scripts[i].filename) { - server.scripts[i].saveScript(script.fileName, script.code, script.hostname, server.scripts); - found = true; - } - } - - if (!found) { - const newScript = new Script(); - newScript.saveScript(script.fileName, script.code, script.hostname, server.scripts); - server.scripts.push(newScript); - } - } - - function onMount(editor: IStandaloneCodeEditor, monaco: Monaco): void { - editorRef.current = editor; - monacoRef.current = monaco; - if (editorRef.current === null) return; - const position = CursorPositions.getCursor(filename); - if (position.row !== -1) - editorRef.current.setPosition({ - lineNumber: position.row, - column: position.column, - }); - editorRef.current.focus(); - - const script = getOpenedScript(filename, props.player.getCurrentServer().hostname); - - // Check if script is already opened, if so switch to that model - if (script !== null) { - if (script.model.isDisposed()) { - regenerateModel(script); - } - - setCurrentScript(script); - } else { - if (filename !== undefined) { - // Create new model - if (monacoRef.current !== null) { - const newScript = new openScript( - filename, - code, - props.player.getCurrentServer().hostname, - new monaco.Position(0, 0), - monacoRef.current.editor.createModel(code, "javascript"), - ); - setCurrentScript(newScript); - openScripts.push(newScript); - } - } else { - // Script Editor was opened by the sidebar button - if (currentScript.model !== undefined) { - if (currentScript.model.isDisposed()) { - // Create new model, old one was disposed of - regenerateModel(currentScript); - } - - setCurrentScript(currentScript); - } else { - // Create a new temporary file - if (monacoRef.current !== null) { - const newScript = new openScript( - "newfile.script", - "", - props.player.getCurrentServer().hostname, - new monaco.Position(0, 0), - monacoRef.current.editor.createModel("", "javascript"), - ); - setCurrentScript(newScript); - openScripts.push(newScript); + let serverScriptIndex = GetServer(closingScript.hostname)?.scripts.findIndex(script => script.filename === closingScript.fileName); + if (serverScriptIndex === -1 || savedScriptCode !== GetServer(closingScript.hostname)?.scripts[serverScriptIndex as number].code) { + PromptEvent.emit({ + txt: 'Do you want to save changes to ' + closingScript.fileName + '?', + resolve: (result: boolean) => { + if (result) { + // Save changes + closingScript.code = savedScriptCode; + saveScript(closingScript); } } - } - } - } - - function beforeMount(monaco: any): void { - if (symbolsLoaded) return; - symbolsLoaded = true; - monaco.languages.registerCompletionItemProvider("javascript", { - provideCompletionItems: () => { - const suggestions = []; - for (const symbol of symbols) { - suggestions.push({ - label: symbol, - kind: monaco.languages.CompletionItemKind.Function, - insertText: symbol, - insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, - }); - } - return { suggestions: suggestions }; - }, - }); - (async function () { - // We have to improve the default js language otherwise theme sucks - const l = await monaco.languages - .getLanguages() - .find((l: any) => l.id === "javascript") - .loader(); - l.language.tokenizer.root.unshift(["ns", { token: "ns" }]); - for (const symbol of symbols) l.language.tokenizer.root.unshift([symbol, { token: "netscriptfunction" }]); - const otherKeywords = ["let", "const", "var", "function"]; - const otherKeyvars = ["true", "false", "null", "undefined"]; - otherKeywords.forEach((k) => l.language.tokenizer.root.unshift([k, { token: "otherkeywords" }])); - otherKeyvars.forEach((k) => l.language.tokenizer.root.unshift([k, { token: "otherkeyvars" }])); - l.language.tokenizer.root.unshift(["this", { token: "this" }]); - })(); - - const source = (libSource + "").replace(/export /g, ""); - monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts"); - monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts"); - loadThemes(monaco); - } - - // Change tab highlight from old tab to new tab - function changeTabButtonColor( - oldButtonFileName: string, - oldButtonHostname: string, - newButtonFileName: string, - newButtonHostname: string, - ): void { - const oldTabButton = document.getElementById("tabButton" + oldButtonFileName + oldButtonHostname); - if (oldTabButton !== null) { - oldTabButton.style.backgroundColor = ""; + }) } - const oldTabCloseButton = document.getElementById("tabCloseButton" + oldButtonFileName + oldButtonHostname); - if (oldTabCloseButton !== null) { - oldTabCloseButton.style.backgroundColor = ""; - } + if (openScripts.length > 1) { + setOpenScripts(oldScripts => oldScripts.filter((value, i) => i !== index)); - const newTabButton = document.getElementById("tabButton" + newButtonFileName + newButtonHostname); - if (newTabButton !== null) { - newTabButton.style.backgroundColor = "#666"; - } - - const newTabCloseButton = document.getElementById("tabCloseButton" + newButtonFileName + newButtonHostname); - if (newTabCloseButton !== null) { - newTabCloseButton.style.backgroundColor = "#666"; - } - } - - // Called when a script tab was clicked - function onTabButtonClick(e: React.MouseEvent): void { - const valSplit = e.currentTarget.value.split(":"); - const fileName = valSplit[0]; - const hostname = valSplit[1]; - - // Change tab highlight from old tab to new tab - changeTabButtonColor(currentScript.fileName, currentScript.hostname, fileName, hostname); - - // Update current script - const clickedScript = getOpenedScript(fileName, hostname); - - if (clickedScript !== null) { - if (clickedScript.model.isDisposed()) { - regenerateModel(clickedScript); + let indexOffset = -1; + if (openScripts[index + indexOffset] === undefined) { + indexOffset = 1; } - setCurrentScript(clickedScript); - } - } - - // Called when a script tab close button was clicked - function onCloseButtonClick(e: React.MouseEvent): void { - const valSplit = e.currentTarget.value.split(":"); - const fileName = valSplit[0]; - const hostname = valSplit[1]; - - const scriptToClose = getOpenedScript(fileName, hostname); - - // Save and remove script from openScripts - if (scriptToClose !== null) { - saveScript(scriptToClose); - - openScripts.splice(openScripts.indexOf(scriptToClose), 1); - } - - if (openScripts.length === 0) { - // No other scripts are open, create a new temporary file - if (monacoRef.current !== null) { - const newScript = new openScript( - "newfile.script", - "", - props.player.getCurrentServer().hostname, - new monacoRef.current.Position(0, 0), - monacoRef.current.editor.createModel("", "javascript"), - ); - - setCurrentScript(newScript); - openScripts.push(newScript); - - // Modify button for temp file - const parent = e.currentTarget.parentElement; - if (parent !== null) { - (parent.children[0] as HTMLButtonElement).value = "newfile.script:home"; - (parent.children[0] as HTMLButtonElement).textContent = "newfile.script"; - e.currentTarget.value = "newfile.script:home"; + // Change current script if we closed it + setCurrentScript(openScripts[index + indexOffset]); + if (editorRef.current !== null) { + if (openScripts[index + indexOffset].model === undefined || openScripts[index + indexOffset].model === null || openScripts[index + indexOffset].model.isDisposed()) { + regenerateModel(openScripts[index + indexOffset]); } + + editorRef.current.setModel(openScripts[index + indexOffset].model); + editorRef.current.setPosition(openScripts[index + indexOffset].lastPosition); + editorRef.current.revealLineInCenter(openScripts[index + indexOffset].lastPosition.lineNumber) } } else { - if (openScripts[0].model.isDisposed()) { - regenerateModel(openScripts[0]); - } - - changeTabButtonColor( - currentScript.fileName, - currentScript.hostname, - openScripts[0].fileName, - openScripts[0].hostname, - ); - - setCurrentScript(openScripts[0]); - } - } - - // Generate a button for each open script - const scriptButtons = []; - for (let i = 0; i < openScripts.length; i++) { - if (openScripts[i].fileName !== "") { - const fileName2 = openScripts[i].fileName; - const hostname = openScripts[i].hostname; - if (openScripts[i].fileName === currentScript.fileName && openScripts[i].hostname === currentScript.hostname) { - // Set special background color for current script tab button - scriptButtons.push( - - {hostname}:~/{fileName2} - - } - > -
- - -
-
, - ); - } else { - scriptButtons.push( -
- - -
, - ); - } + // No more scripts are open + setOpenScripts([]); + setCurrentScript(null); } } @@ -605,59 +562,112 @@ export function Root(props: IProps): React.ReactElement { const p = 11000 / -window.innerHeight + 100; return ( <> - - {scriptButtons} - - Loading script editor!} - height={p + "%"} - defaultLanguage="javascript" - defaultValue={code} - onChange={updateCode} - theme={options.theme} - options={{ ...options, glyphMargin: true }} - /> - - - - {ram} - - - - {" "} - Documentation:{" "} - - Basic - {" "} - | - - Full - - - setOptionsOpen(true)}> - <> - - options - - - - setOptionsOpen(false)} - options={{ - theme: Settings.MonacoTheme, - insertSpaces: Settings.MonacoInsertSpaces, - fontSize: Settings.MonacoFontSize, - }} - save={(options: Options) => { - setOptions(options); - Settings.MonacoTheme = options.theme; - Settings.MonacoInsertSpaces = options.insertSpaces; - Settings.MonacoFontSize = options.fontSize; - }} - /> +
+ + + {(provided, snapshot) => ( + + {openScripts.map(({ fileName, hostname }, index) => ( + + {(provided, snapshot) => ( +
+ + +
+ )} +
+ ))} + {provided.placeholder} +
+ )} +
+
+
+ Loading script editor!} + height={p + "%"} + defaultLanguage="javascript" + defaultValue={''} + onChange={updateCode} + theme={options.theme} + options={{ ...options, glyphMargin: true }} + /> + + + + {ram} + + + + {" "} + Documentation:{" "} + + Basic + {" "} + | + + Full + + + setOptionsOpen(true)}> + <> + + options + + + + setOptionsOpen(false)} + options={{ + theme: Settings.MonacoTheme, + insertSpaces: Settings.MonacoInsertSpaces, + fontSize: Settings.MonacoFontSize, + }} + save={(options: Options) => { + setOptions(options); + Settings.MonacoTheme = options.theme; + Settings.MonacoInsertSpaces = options.insertSpaces; + Settings.MonacoFontSize = options.fontSize; + }} + /> +
+
+

No open files

Use "nano [File Name]" in the terminal to open files

+
- ); + ) } diff --git a/src/ui/React/PromptManager.tsx b/src/ui/React/PromptManager.tsx index dbe405fa9..113131812 100644 --- a/src/ui/React/PromptManager.tsx +++ b/src/ui/React/PromptManager.tsx @@ -43,8 +43,10 @@ export function PromptManager(): React.ReactElement { {prompt != null && ( {prompt.txt} - - +
+ + +
)}