mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-26 09:33:49 +01:00
Merge pull request #1977 from Rez855/feature/multi-tab-script-editor
Added the ability to open multiple script files at a time
This commit is contained in:
commit
853125009b
@ -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" paddingBottom='5px'>
|
||||||
<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,10 +557,10 @@ 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 sx={{ mx: 1 }}>
|
||||||
{" "}
|
{" "}
|
||||||
Documentation:{" "}
|
Documentation:{" "}
|
||||||
@ -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}
|
||||||
@ -435,4 +596,4 @@ export function Root(props: IProps): React.ReactElement {
|
|||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user