mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-29 19:13:49 +01:00
Added the ability to open multiple script files at a time
Fixed a bug where ram would stay the same when switching between scripts Fixed bug where updating ram could cause the GUI to crash The editor will now go to the last edited line when switching tabs. Tidied up the script editor UI
This commit is contained in:
parent
eb002d655a
commit
2265f797c4
@ -1,7 +1,8 @@
|
|||||||
import React, { useState, useEffect, useRef, useMemo } from "react";
|
import React, { useState, useEffect, useRef, useMemo } from "react";
|
||||||
import Editor from "@monaco-editor/react";
|
import Editor, { Monaco } from "@monaco-editor/react";
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
type IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
|
||||||
|
type ITextModel = monaco.editor.ITextModel;
|
||||||
import { OptionsModal } from "./OptionsModal";
|
import { OptionsModal } from "./OptionsModal";
|
||||||
import { Options } from "./Options";
|
import { Options } from "./Options";
|
||||||
import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
|
import { isValidFilePath } from "../../Terminal/DirectoryHelpers";
|
||||||
@ -34,6 +35,10 @@ import IconButton from "@mui/material/IconButton";
|
|||||||
import SettingsIcon from "@mui/icons-material/Settings";
|
import SettingsIcon from "@mui/icons-material/Settings";
|
||||||
|
|
||||||
import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
|
import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
|
||||||
|
import { cssNumber } from "cypress/types/jquery";
|
||||||
|
import { buttonBaseClasses } from "@mui/material";
|
||||||
|
import { fromPairs } from "cypress/types/lodash";
|
||||||
|
import { StringMatcher } from "cypress/types/net-stubbing";
|
||||||
|
|
||||||
let symbolsLoaded = false;
|
let symbolsLoaded = false;
|
||||||
let symbols: string[] = [];
|
let symbols: string[] = [];
|
||||||
@ -80,22 +85,32 @@ interface IProps {
|
|||||||
// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39
|
// https://github.com/threehams/typescript-error-guide/blob/master/stories/components/Editor.tsx#L11-L39
|
||||||
// https://blog.checklyhq.com/customizing-monaco/
|
// https://blog.checklyhq.com/customizing-monaco/
|
||||||
|
|
||||||
// These variables are used to reload a script when it's clicked on. Because we
|
// Holds all the data for a open script
|
||||||
// won't have references to the old script.
|
class openScript {
|
||||||
let lastFilename = "";
|
fileName: string;
|
||||||
let lastCode = "";
|
code: string;
|
||||||
let hostname = "";
|
hostname: string;
|
||||||
let lastPosition: monaco.Position | null = null;
|
lastPosition: monaco.Position;
|
||||||
|
model: ITextModel;
|
||||||
|
|
||||||
|
constructor(fileName: string, code: string, hostname: string, lastPosition: monaco.Position, model: ITextModel) {
|
||||||
|
this.fileName = fileName;
|
||||||
|
this.code = code;
|
||||||
|
this.hostname = hostname;
|
||||||
|
this.lastPosition = lastPosition;
|
||||||
|
this.model = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openScripts = new Array<openScript>(); // Holds all open scripts
|
||||||
|
let currentScript = {} as openScript; // Script currently being viewed
|
||||||
|
|
||||||
export function Root(props: IProps): React.ReactElement {
|
export function Root(props: IProps): React.ReactElement {
|
||||||
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
|
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
|
||||||
const [filename, setFilename] = useState(props.filename ? props.filename : lastFilename);
|
const monacoRef = useRef<Monaco | null>(null);
|
||||||
const [code, setCode] = useState<string>(props.filename ? props.code : lastCode);
|
const [filename, setFilename] = useState(props.filename);
|
||||||
|
const [code, setCode] = useState<string>(props.code);
|
||||||
const [decorations, setDecorations] = useState<string[]>([]);
|
const [decorations, setDecorations] = useState<string[]>([]);
|
||||||
hostname = props.filename ? props.hostname : hostname;
|
|
||||||
if (hostname === "") {
|
|
||||||
hostname = props.player.getCurrentServer().hostname;
|
|
||||||
}
|
|
||||||
const [ram, setRAM] = useState("RAM: ???");
|
const [ram, setRAM] = useState("RAM: ???");
|
||||||
const [updatingRam, setUpdatingRam] = useState(false);
|
const [updatingRam, setUpdatingRam] = useState(false);
|
||||||
const [optionsOpen, setOptionsOpen] = useState(false);
|
const [optionsOpen, setOptionsOpen] = useState(false);
|
||||||
@ -105,6 +120,8 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
fontSize: Settings.MonacoFontSize,
|
fontSize: Settings.MonacoFontSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const debouncedSetRAM = useMemo(
|
const debouncedSetRAM = useMemo(
|
||||||
() =>
|
() =>
|
||||||
debounce((s) => {
|
debounce((s) => {
|
||||||
@ -114,54 +131,21 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
// store the last known state in case we need to restart without nano.
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.filename === undefined) return;
|
|
||||||
lastFilename = props.filename;
|
|
||||||
lastCode = props.code;
|
|
||||||
lastPosition = null;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
function save(): void {
|
function save(): void {
|
||||||
if (editorRef.current !== null) {
|
|
||||||
const position = editorRef.current.getPosition();
|
|
||||||
if (position !== null) {
|
|
||||||
CursorPositions.saveCursor(filename, {
|
|
||||||
row: position.lineNumber,
|
|
||||||
column: position.column,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastPosition = null;
|
|
||||||
|
|
||||||
// this is duplicate code with saving later.
|
// this is duplicate code with saving later.
|
||||||
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
|
||||||
//Make sure filename + code properly follow tutorial
|
//Make sure filename + code properly follow tutorial
|
||||||
if (filename !== "n00dles.script") {
|
if (currentScript.fileName !== "n00dles.script") {
|
||||||
dialogBoxCreate("Leave the script name as 'n00dles.script'!");
|
dialogBoxCreate("Leave the script name as 'n00dles.script'!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
|
if (currentScript.code.replace(/\s/g, "").indexOf("while(true){hack('n00dles');}") == -1) {
|
||||||
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
dialogBoxCreate("Please copy and paste the code from the tutorial!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save the script
|
//Save the script
|
||||||
const server = GetServer(hostname);
|
saveScript(currentScript);
|
||||||
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 (filename == server.scripts[i].filename) {
|
|
||||||
server.scripts[i].saveScript(filename, code, hostname, server.scripts);
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
const script = new Script();
|
|
||||||
script.saveScript(filename, code, hostname, server.scripts);
|
|
||||||
server.scripts.push(script);
|
|
||||||
}
|
|
||||||
|
|
||||||
iTutorialNextStep();
|
iTutorialNextStep();
|
||||||
|
|
||||||
@ -169,45 +153,43 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename == "") {
|
if (currentScript.fileName == "") {
|
||||||
dialogBoxCreate("You must specify a filename!");
|
dialogBoxCreate("You must specify a filename!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidFilePath(filename)) {
|
if (!isValidFilePath(currentScript.fileName)) {
|
||||||
dialogBoxCreate(
|
dialogBoxCreate(
|
||||||
"Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.",
|
"Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const server = GetServer(hostname);
|
const server = GetServer(currentScript.hostname);
|
||||||
if (server === null) throw new Error("Server should not be null but it is.");
|
if (server === null) throw new Error("Server should not be null but it is.");
|
||||||
if (isScriptFilename(filename)) {
|
if (isScriptFilename(currentScript.fileName)) {
|
||||||
//If the current script already exists on the server, overwrite it
|
//If the current script already exists on the server, overwrite it
|
||||||
for (let i = 0; i < server.scripts.length; i++) {
|
for (let i = 0; i < server.scripts.length; i++) {
|
||||||
if (filename == server.scripts[i].filename) {
|
if (currentScript.fileName == server.scripts[i].filename) {
|
||||||
server.scripts[i].saveScript(filename, code, props.player.currentServer, server.scripts);
|
server.scripts[i].saveScript(currentScript.fileName, currentScript.code, props.player.currentServer, server.scripts);
|
||||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||||
props.router.toTerminal();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//If the current script does NOT exist, create a new one
|
//If the current script does NOT exist, create a new one
|
||||||
const script = new Script();
|
const script = new Script();
|
||||||
script.saveScript(filename, code, props.player.currentServer, server.scripts);
|
script.saveScript(currentScript.fileName, currentScript.code, props.player.currentServer, server.scripts);
|
||||||
server.scripts.push(script);
|
server.scripts.push(script);
|
||||||
} else if (filename.endsWith(".txt")) {
|
} else if (currentScript.fileName.endsWith(".txt")) {
|
||||||
for (let i = 0; i < server.textFiles.length; ++i) {
|
for (let i = 0; i < server.textFiles.length; ++i) {
|
||||||
if (server.textFiles[i].fn === filename) {
|
if (server.textFiles[i].fn === currentScript.fileName) {
|
||||||
server.textFiles[i].write(code);
|
server.textFiles[i].write(currentScript.code);
|
||||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||||
props.router.toTerminal();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const textFile = new TextFile(filename, code);
|
const textFile = new TextFile(currentScript.fileName, currentScript.code);
|
||||||
server.textFiles.push(textFile);
|
server.textFiles.push(textFile);
|
||||||
} else {
|
} else {
|
||||||
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)");
|
dialogBoxCreate("Invalid filename. Must be either a script (.script, .js, or .ns) or " + " or text file (.txt)");
|
||||||
@ -215,7 +197,6 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
if (Settings.SaveGameOnFileSave) saveObject.saveGame();
|
||||||
props.router.toTerminal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function beautify(): void {
|
function beautify(): void {
|
||||||
@ -223,14 +204,9 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
editorRef.current.getAction("editor.action.formatDocument").run();
|
editorRef.current.getAction("editor.action.formatDocument").run();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFilenameChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
|
||||||
lastFilename = event.target.value;
|
|
||||||
setFilename(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function infLoop(newCode: string): void {
|
function infLoop(newCode: string): void {
|
||||||
if (editorRef.current === null) return;
|
if (editorRef.current === null) return;
|
||||||
if (!filename.endsWith(".ns") && !filename.endsWith(".js")) return;
|
if (!currentScript.fileName.endsWith(".ns") && !currentScript.fileName.endsWith(".js")) return;
|
||||||
const awaitWarning = checkInfiniteLoop(newCode);
|
const awaitWarning = checkInfiniteLoop(newCode);
|
||||||
if (awaitWarning !== -1) {
|
if (awaitWarning !== -1) {
|
||||||
const newDecorations = editorRef.current.deltaDecorations(decorations, [
|
const newDecorations = editorRef.current.deltaDecorations(decorations, [
|
||||||
@ -259,20 +235,18 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
function updateCode(newCode?: string): void {
|
function updateCode(newCode?: string): void {
|
||||||
if (newCode === undefined) return;
|
if (newCode === undefined) return;
|
||||||
lastCode = newCode;
|
|
||||||
setCode(newCode);
|
|
||||||
updateRAM(newCode);
|
updateRAM(newCode);
|
||||||
|
currentScript.code = newCode;
|
||||||
try {
|
try {
|
||||||
if (editorRef.current !== null) {
|
if (editorRef.current !== null) {
|
||||||
lastPosition = editorRef.current.getPosition();
|
|
||||||
infLoop(newCode);
|
infLoop(newCode);
|
||||||
}
|
}
|
||||||
} catch (err) {}
|
} catch (err) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate it once the first time the file is loaded.
|
// calculate it once the first time the file is loaded.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
updateRAM(code);
|
updateRAM(currentScript.code);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
async function updateRAM(newCode: string): Promise<void> {
|
async function updateRAM(newCode: string): Promise<void> {
|
||||||
@ -314,8 +288,64 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
return () => document.removeEventListener("keydown", maybeSave);
|
return () => document.removeEventListener("keydown", maybeSave);
|
||||||
});
|
});
|
||||||
|
|
||||||
function onMount(editor: IStandaloneCodeEditor): void {
|
// Generates a new model for the script
|
||||||
|
function regenerateModel(script: openScript) {
|
||||||
|
if (monacoRef.current !== null) {
|
||||||
|
script.model = monacoRef.current.editor.createModel(script.code, 'javascript');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the currently viewed script
|
||||||
|
function setCurrentScript(script: openScript) {
|
||||||
|
// Update last position
|
||||||
|
if (editorRef.current !== null) {
|
||||||
|
if (currentScript !== null) {
|
||||||
|
var 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) {
|
||||||
|
for (const script of openScripts) {
|
||||||
|
if (script.fileName === fileName && script.hostname === hostname) {
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveScript(script: openScript) {
|
||||||
|
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;
|
editorRef.current = editor;
|
||||||
|
monacoRef.current = monaco;
|
||||||
if (editorRef.current === null) return;
|
if (editorRef.current === null) return;
|
||||||
const position = CursorPositions.getCursor(filename);
|
const position = CursorPositions.getCursor(filename);
|
||||||
if (position.row !== -1)
|
if (position.row !== -1)
|
||||||
@ -323,12 +353,44 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
lineNumber: position.row,
|
lineNumber: position.row,
|
||||||
column: position.column,
|
column: position.column,
|
||||||
});
|
});
|
||||||
else if (lastPosition !== null)
|
|
||||||
editorRef.current.setPosition({
|
|
||||||
lineNumber: lastPosition.lineNumber,
|
|
||||||
column: lastPosition.column + 1,
|
|
||||||
});
|
|
||||||
editorRef.current.focus();
|
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) {
|
||||||
|
var 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) {
|
||||||
|
var newScript = new openScript('NewFile.ns', '', props.player.getCurrentServer().hostname, new monaco.Position(0, 0), monacoRef.current.editor.createModel('', 'javascript'));
|
||||||
|
setCurrentScript(newScript);
|
||||||
|
openScripts.push(newScript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function beforeMount(monaco: any): void {
|
function beforeMount(monaco: any): void {
|
||||||
@ -368,26 +430,119 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts");
|
monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts");
|
||||||
loadThemes(monaco);
|
loadThemes(monaco);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Change tab highlight from old tab to new tab
|
||||||
|
function changeTabButtonColor(oldButtonFileName: string, oldButtonHostname: string, newButtonFileName: string, newButtonHostname: string) {
|
||||||
|
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 = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTabButton = document.getElementById('tabButton' + newButtonFileName + newButtonHostname);
|
||||||
|
if (newTabButton !== null) {
|
||||||
|
newTabButton.style.backgroundColor = '#173b2d';
|
||||||
|
}
|
||||||
|
|
||||||
|
const newTabCloseButton = document.getElementById('tabCloseButton' + newButtonFileName + newButtonHostname);
|
||||||
|
if (newTabCloseButton !== null) {
|
||||||
|
newTabCloseButton.style.backgroundColor = '#173b2d';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a script tab was clicked
|
||||||
|
function onTabButtonClick(e: React.MouseEvent<HTMLButtonElement>) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentScript(clickedScript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a script tab close button was clicked
|
||||||
|
function onCloseButtonClick(e: React.MouseEvent<HTMLButtonElement>) {
|
||||||
|
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.ns", '', props.player.getCurrentServer().hostname, new monacoRef.current.Position(0, 0), monacoRef.current.editor.createModel('', 'javascript'));
|
||||||
|
|
||||||
|
setCurrentScript(newScript)
|
||||||
|
openScripts.push(newScript);
|
||||||
|
|
||||||
|
// Create new tab button for temporary file
|
||||||
|
const element = (<div style={{ paddingRight: '5px' }}><Button style={{ backgroundColor: '#173b2d' }} value={newScript.fileName + ':' + newScript.hostname} onClick={onTabButtonClick}>{newScript.fileName}</Button><Button value={newScript.fileName + ':' + newScript.hostname} onClick={onCloseButtonClick} style={{ maxWidth: '20px', minWidth: '20px', backgroundColor: '#173b2d' }}>x</Button></div>)
|
||||||
|
|
||||||
|
// Modify button for temp file
|
||||||
|
var parent = e.currentTarget.parentElement;
|
||||||
|
if (parent !== null) {
|
||||||
|
(parent.children[0] as HTMLButtonElement).value = 'NewFile.ns:home';
|
||||||
|
(parent.children[0] as HTMLButtonElement).textContent = 'NewFile.ns';
|
||||||
|
e.currentTarget.value = 'NewFile.ns:home';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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(<div id={'scriptEditorTab' + fileName2 + hostname} key={'tabButton' + i} style={{ paddingRight: '5px' }}><Button id={'tabButton' + openScripts[i].fileName + openScripts[i].hostname} style={{ backgroundColor: '#173b2d' }} value={fileName2 + ':' + hostname} onClick={onTabButtonClick}>{openScripts[i].fileName}</Button><Button id={'tabCloseButton' + openScripts[i].fileName + openScripts[i].hostname} value={fileName2 + ':' + hostname} onClick={onCloseButtonClick} style={{ maxWidth: '20px', minWidth: '20px', backgroundColor: '#173b2d' }}>x</Button></div>)
|
||||||
|
} else {
|
||||||
|
scriptButtons.push(<div id={'scriptEditorTab' + fileName2 + hostname} key={'tabButton' + i} style={{ paddingRight: '5px' }}><Button id={'tabButton' + openScripts[i].fileName + openScripts[i].hostname} value={fileName2 + ':' + hostname} onClick={onTabButtonClick}>{openScripts[i].fileName}</Button><Button id={'tabCloseButton' + openScripts[i].fileName + openScripts[i].hostname} value={fileName2 + ':' + hostname} onClick={onCloseButtonClick} style={{ maxWidth: '20px', minWidth: '20px' }}>x</Button></div>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 370px 71%, 725px 85.1%, 1085px 90%, 1300px 91.7%
|
// 370px 71%, 725px 85.1%, 1085px 90%, 1300px 91.7%
|
||||||
// fuck around in desmos until you find a function
|
// fuck around in desmos until you find a function
|
||||||
const p = 11000 / -window.innerHeight + 100;
|
const p = 11000 / -window.innerHeight + 100;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box display="flex" flexDirection="row" alignItems="center">
|
<Box display="flex" flexDirection="row" alignItems="center">
|
||||||
<TextField
|
{scriptButtons}
|
||||||
placeholder="filename"
|
|
||||||
type="text"
|
|
||||||
tabIndex={1}
|
|
||||||
value={filename}
|
|
||||||
onChange={onFilenameChange}
|
|
||||||
InputProps={{ startAdornment: <Typography>{hostname}:~/</Typography> }}
|
|
||||||
/>
|
|
||||||
<IconButton onClick={() => setOptionsOpen(true)}>
|
|
||||||
<>
|
|
||||||
<SettingsIcon />
|
|
||||||
options
|
|
||||||
</>
|
|
||||||
</IconButton>
|
|
||||||
</Box>
|
</Box>
|
||||||
<Editor
|
<Editor
|
||||||
beforeMount={beforeMount}
|
beforeMount={beforeMount}
|
||||||
@ -402,11 +557,11 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
/>
|
/>
|
||||||
<Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
|
<Box display="flex" flexDirection="row" sx={{ m: 1 }} alignItems="center">
|
||||||
<Button onClick={beautify}>Beautify</Button>
|
<Button onClick={beautify}>Beautify</Button>
|
||||||
<Typography color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }}>
|
<Typography color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }}>
|
||||||
{ram}
|
{ram}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button onClick={save}>Save & Close (Ctrl/Cmd + b)</Button>
|
<Button onClick={save}>Save (Ctrl/Cmd + b)</Button>
|
||||||
<Typography sx={{ mx: 1 }}>
|
<Typography style={{ marginLeft: '50%' }} sx={{ mx: 1 }}>
|
||||||
{" "}
|
{" "}
|
||||||
Documentation:{" "}
|
Documentation:{" "}
|
||||||
<Link target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
|
<Link target="_blank" href="https://bitburner.readthedocs.io/en/latest/index.html">
|
||||||
@ -417,6 +572,12 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
Full
|
Full
|
||||||
</Link>
|
</Link>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<IconButton style={{ marginLeft: 'auto' }} onClick={() => setOptionsOpen(true)}>
|
||||||
|
<>
|
||||||
|
<SettingsIcon />
|
||||||
|
options
|
||||||
|
</>
|
||||||
|
</IconButton>
|
||||||
</Box>
|
</Box>
|
||||||
<OptionsModal
|
<OptionsModal
|
||||||
open={optionsOpen}
|
open={optionsOpen}
|
||||||
|
Loading…
Reference in New Issue
Block a user