mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-02-17 02:22:23 +01:00
mostly convert terminal to react
This commit is contained in:
@ -5,15 +5,17 @@
|
||||
terminal which has its own page) */
|
||||
|
||||
.generic-menupage-container {
|
||||
padding-left: 2px;
|
||||
-ms-overflow-style: none; /* for Internet Explorer, Edge */
|
||||
scrollbar-width: none; /* for Firefox */
|
||||
flex-grow: 1;
|
||||
height: 100vh;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
#generic-react-container {
|
||||
padding: 10px;
|
||||
overflow-y: scroll;
|
||||
height: 100vh;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
#generic-react-container::-webkit-scrollbar {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { IMap } from "./types";
|
||||
import { post } from "./ui/postToTerminal";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
export let Aliases: IMap<string> = {};
|
||||
export let GlobalAliases: IMap<string> = {};
|
||||
@ -24,12 +24,12 @@ export function loadGlobalAliases(saveString: string): void {
|
||||
export function printAliases(): void {
|
||||
for (const name in Aliases) {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
post("alias " + name + "=" + Aliases[name]);
|
||||
Terminal.print("alias " + name + "=" + Aliases[name]);
|
||||
}
|
||||
}
|
||||
for (const name in GlobalAliases) {
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
post("global alias " + name + "=" + GlobalAliases[name]);
|
||||
Terminal.print("global alias " + name + "=" + GlobalAliases[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ import * as React from "react";
|
||||
import { DarkWebItems } from "./DarkWebItems";
|
||||
|
||||
import { Player } from "../Player";
|
||||
import { Terminal } from "../Terminal";
|
||||
import { SpecialServerIps } from "../Server/SpecialServerIps";
|
||||
import { post, postElement } from "../ui/postToTerminal";
|
||||
import { numeralWrapper } from "../ui/numeralFormat";
|
||||
import { Money } from "../ui/React/Money";
|
||||
|
||||
import { isValidIPAddress } from "../../utils/helpers/isValidIPAddress";
|
||||
@ -16,7 +17,7 @@ export function checkIfConnectedToDarkweb(): void {
|
||||
return;
|
||||
}
|
||||
if (darkwebIp == Player.getCurrentServer().ip) {
|
||||
post(
|
||||
Terminal.print(
|
||||
"You are now connected to the dark web. From the dark web you can purchase illegal items. " +
|
||||
"Use the 'buy -l' command to display a list of all the items you can buy. Use 'buy [item-name] " +
|
||||
"to purchase an item.",
|
||||
@ -35,9 +36,9 @@ export function executeDarkwebTerminalCommand(commandArray: string[]): void {
|
||||
switch (commandArray[0]) {
|
||||
case "buy": {
|
||||
if (commandArray.length != 2) {
|
||||
post("Incorrect number of arguments. Usage: ");
|
||||
post("buy -l");
|
||||
post("buy [item name]");
|
||||
Terminal.error("Incorrect number of arguments. Usage: ");
|
||||
Terminal.print("buy -l");
|
||||
Terminal.print("buy [item name]");
|
||||
return;
|
||||
}
|
||||
const arg = commandArray[1];
|
||||
@ -49,7 +50,7 @@ export function executeDarkwebTerminalCommand(commandArray: string[]): void {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
post("Command not found");
|
||||
Terminal.error("Command not found");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -57,11 +58,7 @@ export function executeDarkwebTerminalCommand(commandArray: string[]): void {
|
||||
export function listAllDarkwebItems(): void {
|
||||
for (const key in DarkWebItems) {
|
||||
const item = DarkWebItems[key];
|
||||
postElement(
|
||||
<>
|
||||
{item.program} - <Money money={item.price} player={Player} /> - {item.description}
|
||||
</>,
|
||||
);
|
||||
Terminal.print(`${item.program} - ${numeralWrapper.formatMoney(item.price)} - ${item.description}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,24 +76,26 @@ export function buyDarkwebItem(itemName: string): void {
|
||||
|
||||
// return if invalid
|
||||
if (item === null) {
|
||||
post("Unrecognized item: " + itemName);
|
||||
Terminal.print("Unrecognized item: " + itemName);
|
||||
return;
|
||||
}
|
||||
|
||||
// return if the player already has it.
|
||||
if (Player.hasProgram(item.program)) {
|
||||
post("You already have the " + item.program + " program");
|
||||
Terminal.print("You already have the " + item.program + " program");
|
||||
return;
|
||||
}
|
||||
|
||||
// return if the player doesn't have enough money
|
||||
if (Player.money.lt(item.price)) {
|
||||
post("Not enough money to purchase " + item.program);
|
||||
Terminal.print("Not enough money to purchase " + item.program);
|
||||
return;
|
||||
}
|
||||
|
||||
// buy and push
|
||||
Player.loseMoney(item.price);
|
||||
Player.getHomeComputer().programs.push(item.program);
|
||||
post("You have purchased the " + item.program + " program. The new program can be found on your home computer.");
|
||||
Terminal.print(
|
||||
"You have purchased the " + item.program + " program. The new program can be found on your home computer.",
|
||||
);
|
||||
}
|
||||
|
@ -1590,7 +1590,7 @@ HackingMission.prototype.finishMission = function (win) {
|
||||
Mission won! You earned {Reputation(gain)} reputation with {this.faction.name}
|
||||
</>,
|
||||
);
|
||||
Player.gainIntelligenceExp(this.difficulty * CONSTANTS.IntelligenceHackingMissionBaseExpGain);
|
||||
Player.gainIntelligenceExp(Math.pow(this.difficulty * CONSTANTS.IntelligenceHackingMissionBaseExpGain, 0.5));
|
||||
this.faction.playerReputation += gain;
|
||||
} else {
|
||||
dialogBoxCreate("Mission lost/forfeited! You did not gain any faction reputation.");
|
||||
|
@ -145,7 +145,6 @@ import { findSleevePurchasableAugs } from "./PersonObjects/Sleeve/SleeveHelpers"
|
||||
import { Exploit } from "./Exploits/Exploit.ts";
|
||||
|
||||
import { numeralWrapper } from "./ui/numeralFormat";
|
||||
import { post } from "./ui/postToTerminal";
|
||||
import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
import { is2DArray } from "./utils/helpers/is2DArray";
|
||||
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
|
||||
@ -1059,10 +1058,10 @@ function NetscriptFunctions(workerScript) {
|
||||
if (arguments.length === 0) {
|
||||
throw makeRuntimeErrorMsg("tprint", "Takes at least 1 argument.");
|
||||
}
|
||||
post(`${workerScript.scriptRef.filename}: ${argsToString(arguments)}`);
|
||||
Terminal.print(`${workerScript.scriptRef.filename}: ${argsToString(arguments)}`);
|
||||
},
|
||||
tprintf: function (format, ...args) {
|
||||
post(vsprintf(format, args));
|
||||
Terminal.print(vsprintf(format, args));
|
||||
},
|
||||
clearLog: function () {
|
||||
workerScript.scriptRef.clearLog();
|
||||
@ -3134,8 +3133,7 @@ function NetscriptFunctions(workerScript) {
|
||||
Player.getCurrentServer().isConnectedTo = false;
|
||||
Player.currentServer = Player.getHomeComputer().ip;
|
||||
Player.getCurrentServer().isConnectedTo = true;
|
||||
Terminal.currDir = "/";
|
||||
Terminal.resetTerminalInput(true);
|
||||
Terminal.setcwd("/");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3146,8 +3144,7 @@ function NetscriptFunctions(workerScript) {
|
||||
Player.getCurrentServer().isConnectedTo = false;
|
||||
Player.currentServer = target.ip;
|
||||
Player.getCurrentServer().isConnectedTo = true;
|
||||
Terminal.currDir = "/";
|
||||
Terminal.resetTerminalInput(true);
|
||||
Terminal.setcwd("/");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ import { prestigeHomeComputer } from "./Server/ServerHelpers";
|
||||
import { SourceFileFlags, updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { SpecialServerIps, prestigeSpecialServerIps, SpecialServerNames } from "./Server/SpecialServerIps";
|
||||
import { deleteStockMarket, initStockMarket, initSymbolToStockMap } from "./StockMarket/StockMarket";
|
||||
import { Terminal, postVersion } from "./Terminal";
|
||||
import { Terminal } from "./Terminal";
|
||||
|
||||
import { Page, routing } from "./ui/navigationTracking";
|
||||
|
||||
@ -65,13 +65,8 @@ function prestigeAugmentation() {
|
||||
|
||||
Player.factions = Player.factions.concat(maintainMembership);
|
||||
|
||||
// Now actually go to the Terminal Screen (and reset it)
|
||||
var mainMenu = document.getElementById("mainmenu-container");
|
||||
mainMenu.style.visibility = "visible";
|
||||
Terminal.resetTerminalInput();
|
||||
Terminal.clear();
|
||||
Engine.loadTerminalContent();
|
||||
$("#terminal tr:not(:last)").remove();
|
||||
postVersion();
|
||||
|
||||
// Delete all Worker Scripts objects
|
||||
prestigeWorkerScripts();
|
||||
|
@ -6,7 +6,7 @@ import { Script } from "./Script";
|
||||
import { FconfSettings } from "../Fconf/FconfSettings";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { IMap } from "../types";
|
||||
import { post } from "../ui/postToTerminal";
|
||||
import { Terminal } from "../Terminal";
|
||||
|
||||
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../../utils/JSONReviver";
|
||||
import { getTimestamp } from "../../utils/helpers/getTimestamp";
|
||||
@ -85,7 +85,7 @@ export class RunningScript {
|
||||
|
||||
displayLog(): void {
|
||||
for (let i = 0; i < this.logs.length; ++i) {
|
||||
post(this.logs[i]);
|
||||
Terminal.print(this.logs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
2
src/Terminal.d.ts
vendored
2
src/Terminal.d.ts
vendored
@ -1 +1 @@
|
||||
export declare const Terminal: any;
|
||||
export declare const Terminal: ITerminal;
|
||||
|
378
src/Terminal.jsx
378
src/Terminal.jsx
@ -9,235 +9,25 @@ import { setTimeoutRef } from "./utils/SetTimeoutRef";
|
||||
import { Page, routing } from "./ui/navigationTracking";
|
||||
import { KEY } from "../utils/helpers/keyCodes";
|
||||
import { getTimestamp } from "../utils/helpers/getTimestamp";
|
||||
import { post } from "./ui/postToTerminal";
|
||||
|
||||
import { Terminal as TTerminal } from "./Terminal/Terminal";
|
||||
|
||||
const NewTerminal = new TTerminal();
|
||||
|
||||
import autosize from "autosize";
|
||||
|
||||
function postVersion() {
|
||||
post("Bitburner v" + CONSTANTS.Version);
|
||||
}
|
||||
|
||||
function getTerminalInput() {
|
||||
return document.getElementById("terminal-input-text-box").value;
|
||||
}
|
||||
const Terminal = new TTerminal();
|
||||
|
||||
// Defines key commands in terminal
|
||||
$(document).keydown(function (event) {
|
||||
// Terminal
|
||||
if (routing.isOn(Page.Terminal)) {
|
||||
var terminalInput = document.getElementById("terminal-input-text-box");
|
||||
if (terminalInput != null && !event.ctrlKey && !event.shiftKey && !NewTerminal.contractOpen) {
|
||||
terminalInput.focus();
|
||||
|
||||
if (event.keyCode === KEY.C && event.ctrlKey) {
|
||||
if (Engine._actionInProgress) {
|
||||
// Cancel action
|
||||
// post("Cancelling...");
|
||||
Engine._actionInProgress = false;
|
||||
Terminal.finishAction(true);
|
||||
} else if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
// Dont prevent default so it still copies
|
||||
Terminal.clear(); // Clear Terminal
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.ENTER) {
|
||||
event.preventDefault(); // Prevent newline from being entered in Script Editor
|
||||
const command = getTerminalInput();
|
||||
const dir = NewTerminal.currDir;
|
||||
post(
|
||||
"<span class='prompt'>[" +
|
||||
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
|
||||
Player.getCurrentServer().hostname +
|
||||
` ~${dir}]></span> ${command}`,
|
||||
);
|
||||
|
||||
if (command.length > 0) {
|
||||
Terminal.resetTerminalInput(); // Clear input first
|
||||
NewTerminal.executeCommands(Engine, Player, command);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.C && event.ctrlKey) {
|
||||
if (Engine._actionInProgress) {
|
||||
// Cancel action
|
||||
post("Cancelling...");
|
||||
Engine._actionInProgress = false;
|
||||
NewTerminal.finishAction(true);
|
||||
} else if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
// Dont prevent default so it still copies
|
||||
Terminal.resetTerminalInput(); // Clear Terminal
|
||||
}
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.L && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
NewTerminal.executeCommands(Engine, Player, "clear"); // Clear screen
|
||||
}
|
||||
|
||||
// Ctrl p same as up arrow
|
||||
// Ctrl n same as down arrow
|
||||
|
||||
if (
|
||||
event.keyCode === KEY.UPARROW ||
|
||||
(FconfSettings.ENABLE_BASH_HOTKEYS && event.keyCode === KEY.P && event.ctrlKey)
|
||||
) {
|
||||
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
event.preventDefault();
|
||||
}
|
||||
// Cycle through past commands
|
||||
if (terminalInput == null) {
|
||||
return;
|
||||
}
|
||||
var i = NewTerminal.commandHistoryIndex;
|
||||
var len = NewTerminal.commandHistory.length;
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
if (i < 0 || i > len) {
|
||||
NewTerminal.commandHistoryIndex = len;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
--NewTerminal.commandHistoryIndex;
|
||||
}
|
||||
var prevCommand = NewTerminal.commandHistory[NewTerminal.commandHistoryIndex];
|
||||
terminalInput.value = prevCommand;
|
||||
setTimeoutRef(function () {
|
||||
terminalInput.selectionStart = terminalInput.selectionEnd = 10000;
|
||||
}, 10);
|
||||
}
|
||||
|
||||
if (
|
||||
event.keyCode === KEY.DOWNARROW ||
|
||||
(FconfSettings.ENABLE_BASH_HOTKEYS && event.keyCode === KEY.M && event.ctrlKey)
|
||||
) {
|
||||
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
event.preventDefault();
|
||||
}
|
||||
// Cycle through past commands
|
||||
if (terminalInput == null) {
|
||||
return;
|
||||
}
|
||||
var i = NewTerminal.commandHistoryIndex;
|
||||
var len = NewTerminal.commandHistory.length;
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
if (i < 0 || i > len) {
|
||||
NewTerminal.commandHistoryIndex = len;
|
||||
}
|
||||
|
||||
// Latest command, put nothing
|
||||
if (i == len || i == len - 1) {
|
||||
NewTerminal.commandHistoryIndex = len;
|
||||
terminalInput.value = "";
|
||||
} else {
|
||||
++NewTerminal.commandHistoryIndex;
|
||||
var prevCommand = NewTerminal.commandHistory[NewTerminal.commandHistoryIndex];
|
||||
terminalInput.value = prevCommand;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.TAB) {
|
||||
event.preventDefault();
|
||||
|
||||
// Autocomplete
|
||||
if (terminalInput == null) {
|
||||
return;
|
||||
}
|
||||
let input = terminalInput.value;
|
||||
if (input == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const semiColonIndex = input.lastIndexOf(";");
|
||||
if (semiColonIndex !== -1) {
|
||||
input = input.slice(semiColonIndex + 1);
|
||||
}
|
||||
|
||||
input = input.trim();
|
||||
input = input.replace(/\s\s+/g, " ");
|
||||
|
||||
const commandArray = input.split(" ");
|
||||
let index = commandArray.length - 2;
|
||||
if (index < -1) {
|
||||
index = 0;
|
||||
}
|
||||
const allPos = determineAllPossibilitiesForTabCompletion(Player, input, index, NewTerminal.currDir);
|
||||
if (allPos.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let arg = "";
|
||||
let command = "";
|
||||
if (commandArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (commandArray.length == 1) {
|
||||
command = commandArray[0];
|
||||
} else if (commandArray.length == 2) {
|
||||
command = commandArray[0];
|
||||
arg = commandArray[1];
|
||||
} else if (commandArray.length == 3) {
|
||||
command = commandArray[0] + " " + commandArray[1];
|
||||
arg = commandArray[2];
|
||||
} else {
|
||||
arg = commandArray.pop();
|
||||
command = commandArray.join(" ");
|
||||
}
|
||||
|
||||
tabCompletion(command, arg, allPos);
|
||||
terminalInput.focus();
|
||||
}
|
||||
|
||||
// Extra Bash Emulation Hotkeys, must be enabled through .fconf
|
||||
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
if (event.keyCode === KEY.A && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
Terminal.moveTextCursor("home");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.E && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
Terminal.moveTextCursor("end");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.B && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
Terminal.moveTextCursor("prevchar");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.B && event.altKey) {
|
||||
event.preventDefault();
|
||||
Terminal.moveTextCursor("prevword");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.F && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
Terminal.moveTextCursor("nextchar");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.F && event.altKey) {
|
||||
event.preventDefault();
|
||||
Terminal.moveTextCursor("nextword");
|
||||
}
|
||||
|
||||
if ((event.keyCode === KEY.H || event.keyCode === KEY.D) && event.ctrlKey) {
|
||||
Terminal.modifyInput("backspace");
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// TODO AFTER THIS:
|
||||
// alt + d deletes word after cursor
|
||||
// ^w deletes word before cursor
|
||||
// ^k clears line after cursor
|
||||
// ^u clears line before cursor
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Keep terminal in focus
|
||||
let terminalCtrlPressed = false,
|
||||
shiftKeyPressed = false;
|
||||
$(document).ready(function () {
|
||||
if (routing.isOn(Page.Terminal)) {
|
||||
$(".terminal-input").focus();
|
||||
}
|
||||
});
|
||||
|
||||
@ -247,7 +37,7 @@ $(document).keydown(function (e) {
|
||||
terminalCtrlPressed = true;
|
||||
} else if (e.shiftKey) {
|
||||
shiftKeyPressed = true;
|
||||
} else if (terminalCtrlPressed || shiftKeyPressed || NewTerminal.contractOpen) {
|
||||
} else if (terminalCtrlPressed || shiftKeyPressed || Terminal.contractOpen) {
|
||||
// Don't focus
|
||||
} else {
|
||||
var inputTextBox = document.getElementById("terminal-input-text-box");
|
||||
@ -261,146 +51,4 @@ $(document).keydown(function (e) {
|
||||
}
|
||||
});
|
||||
|
||||
$(document).keyup(function (e) {
|
||||
if (routing.isOn(Page.Terminal)) {
|
||||
if (e.which == KEY.CTRL) {
|
||||
terminalCtrlPressed = false;
|
||||
}
|
||||
if (e.shiftKey) {
|
||||
shiftKeyPressed = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let Terminal = {
|
||||
resetTerminalInput: function (keepInput = false) {
|
||||
let input = "";
|
||||
if (keepInput) {
|
||||
input = getTerminalInput();
|
||||
}
|
||||
const dir = NewTerminal.currDir;
|
||||
if (FconfSettings.WRAP_INPUT) {
|
||||
document.getElementById("terminal-input-td").innerHTML =
|
||||
`<div id='terminal-input-header' class='prompt'>[${Player.getCurrentServer().hostname} ~${dir}]$ </div>` +
|
||||
`<textarea type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" value=\"${input}\" autocomplete="off" />`;
|
||||
|
||||
// Auto re-size the line element as it wraps
|
||||
autosize(document.getElementById("terminal-input-text-box"));
|
||||
} else {
|
||||
document.getElementById("terminal-input-td").innerHTML =
|
||||
`<div id='terminal-input-header' class='prompt'>[${Player.getCurrentServer().hostname} ~${dir}]$ </div>` +
|
||||
`<input type="text" id="terminal-input-text-box" class="terminal-input" tabindex="1" value=\"${input}\" autocomplete="off" />`;
|
||||
}
|
||||
const hdr = document.getElementById("terminal-input-header");
|
||||
hdr.style.display = "inline";
|
||||
|
||||
const terminalInput = document.getElementById("terminal-input-text-box");
|
||||
if (typeof terminalInput.selectionStart == "number") {
|
||||
terminalInput.selectionStart = terminalInput.selectionEnd = terminalInput.value.length;
|
||||
} else if (typeof terminalInput.createTextRange != "undefined") {
|
||||
terminalInput.focus();
|
||||
var range = el.createTextRange();
|
||||
range.collapse(false);
|
||||
range.select();
|
||||
}
|
||||
},
|
||||
|
||||
modifyInput: function (mod) {
|
||||
try {
|
||||
var terminalInput = document.getElementById("terminal-input-text-box");
|
||||
if (terminalInput == null) {
|
||||
return;
|
||||
}
|
||||
terminalInput.focus();
|
||||
|
||||
var inputLength = terminalInput.value.length;
|
||||
var start = terminalInput.selectionStart;
|
||||
var inputText = terminalInput.value;
|
||||
|
||||
switch (mod.toLowerCase()) {
|
||||
case "backspace":
|
||||
if (start > 0 && start <= inputLength + 1) {
|
||||
terminalInput.value = inputText.substr(0, start - 1) + inputText.substr(start);
|
||||
}
|
||||
break;
|
||||
case "deletewordbefore": // Delete rest of word before the cursor
|
||||
for (var delStart = start - 1; delStart > 0; --delStart) {
|
||||
if (inputText.charAt(delStart) === " ") {
|
||||
terminalInput.value = inputText.substr(0, delStart) + inputText.substr(start);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "deletewordafter": // Delete rest of word after the cursor
|
||||
for (var delStart = start + 1; delStart <= text.length + 1; ++delStart) {
|
||||
if (inputText.charAt(delStart) === " ") {
|
||||
terminalInput.value = inputText.substr(0, start) + inputText.substr(delStart);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "clearafter": // Deletes everything after cursor
|
||||
break;
|
||||
case "clearbefore:": // Deleetes everything before cursor
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Exception in Terminal.modifyInput: " + e);
|
||||
}
|
||||
},
|
||||
|
||||
moveTextCursor: function (loc) {
|
||||
try {
|
||||
var terminalInput = document.getElementById("terminal-input-text-box");
|
||||
if (terminalInput == null) {
|
||||
return;
|
||||
}
|
||||
terminalInput.focus();
|
||||
|
||||
var inputLength = terminalInput.value.length;
|
||||
var start = terminalInput.selectionStart;
|
||||
|
||||
switch (loc.toLowerCase()) {
|
||||
case "home":
|
||||
terminalInput.setSelectionRange(0, 0);
|
||||
break;
|
||||
case "end":
|
||||
terminalInput.setSelectionRange(inputLength, inputLength);
|
||||
break;
|
||||
case "prevchar":
|
||||
if (start > 0) {
|
||||
terminalInput.setSelectionRange(start - 1, start - 1);
|
||||
}
|
||||
break;
|
||||
case "prevword":
|
||||
for (var i = start - 2; i >= 0; --i) {
|
||||
if (terminalInput.value.charAt(i) === " ") {
|
||||
terminalInput.setSelectionRange(i + 1, i + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
terminalInput.setSelectionRange(0, 0);
|
||||
break;
|
||||
case "nextchar":
|
||||
terminalInput.setSelectionRange(start + 1, start + 1);
|
||||
break;
|
||||
case "nextword":
|
||||
for (var i = start + 1; i <= inputLength; ++i) {
|
||||
if (terminalInput.value.charAt(i) === " ") {
|
||||
terminalInput.setSelectionRange(i, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
terminalInput.setSelectionRange(inputLength, inputLength);
|
||||
break;
|
||||
default:
|
||||
console.warn("Invalid loc argument in Terminal.moveTextCursor()");
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Exception in Terminal.moveTextCursor: " + e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export { postVersion, Terminal };
|
||||
export { Terminal };
|
||||
|
@ -3,6 +3,11 @@ import { Script } from "../Script/Script";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { IEngine } from "../IEngine";
|
||||
|
||||
export interface IOutput {
|
||||
text: string;
|
||||
color: "inherit" | "initial" | "primary" | "secondary" | "error" | "textPrimary" | "textSecondary" | undefined;
|
||||
}
|
||||
|
||||
export interface ITerminal {
|
||||
// Flags to determine whether the player is currently running a hack or an analyze
|
||||
hackFlag: boolean;
|
||||
@ -14,6 +19,8 @@ export interface ITerminal {
|
||||
commandHistory: string[];
|
||||
commandHistoryIndex: number;
|
||||
|
||||
outputHistory: IOutput[];
|
||||
|
||||
// True if a Coding Contract prompt is opened
|
||||
contractOpen: boolean;
|
||||
|
||||
@ -24,6 +31,7 @@ export interface ITerminal {
|
||||
print(s: string, config?: any): void;
|
||||
error(s: string): void;
|
||||
|
||||
clear(): void;
|
||||
startAnalyze(): void;
|
||||
startBackdoor(player: IPlayer): void;
|
||||
startHack(player: IPlayer): void;
|
||||
@ -36,7 +44,6 @@ export interface ITerminal {
|
||||
getScript(player: IPlayer, filename: string): Script | null;
|
||||
getTextFile(player: IPlayer, filename: string): TextFile | null;
|
||||
getLitFile(player: IPlayer, filename: string): string | null;
|
||||
resetTerminalInput(): void;
|
||||
cwd(): string;
|
||||
setcwd(dir: string): void;
|
||||
runContract(player: IPlayer, name: string): void;
|
||||
@ -44,4 +51,6 @@ export interface ITerminal {
|
||||
connectToServer(player: IPlayer, server: string): void;
|
||||
executeCommand(engine: IEngine, player: IPlayer, command: string): void;
|
||||
executeCommands(engine: IEngine, player: IPlayer, commands: string): void;
|
||||
// If there was any changes, will return true, once.
|
||||
pollChanges(): boolean;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { postContent, hackProgressBarPost, hackProgressPost } from "../ui/postToTerminal";
|
||||
import { ITerminal } from "./ITerminal";
|
||||
import { ITerminal, IOutput } from "./ITerminal";
|
||||
import { IEngine } from "../IEngine";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { HacknetServer } from "../Hacknet/HacknetServer";
|
||||
@ -71,6 +71,7 @@ import { wget } from "./commands/wget";
|
||||
import { clear } from "./commands/clear";
|
||||
|
||||
export class Terminal implements ITerminal {
|
||||
hasChanges = false;
|
||||
// Flags to determine whether the player is currently running a hack or an analyze
|
||||
hackFlag = false;
|
||||
backdoorFlag = false;
|
||||
@ -81,6 +82,8 @@ export class Terminal implements ITerminal {
|
||||
commandHistory: string[] = [];
|
||||
commandHistoryIndex = 0;
|
||||
|
||||
outputHistory: IOutput[] = [{ text: `Bitburner v${CONSTANTS.Version}`, color: "primary" }];
|
||||
|
||||
// True if a Coding Contract prompt is opened
|
||||
contractOpen = false;
|
||||
|
||||
@ -88,12 +91,22 @@ export class Terminal implements ITerminal {
|
||||
// Excludes the trailing forward slash
|
||||
currDir = "/";
|
||||
|
||||
pollChanges(): boolean {
|
||||
if (this.hasChanges) {
|
||||
this.hasChanges = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
print(s: string, config?: any): void {
|
||||
postContent(s, config);
|
||||
this.outputHistory.push({ text: s, color: "primary" });
|
||||
this.hasChanges = true;
|
||||
}
|
||||
|
||||
error(s: string): void {
|
||||
postContent(`ERROR: ${s}`, { color: "#ff2929" });
|
||||
this.outputHistory.push({ text: s, color: "error" });
|
||||
this.hasChanges = true;
|
||||
}
|
||||
|
||||
startHack(player: IPlayer): void {
|
||||
@ -251,7 +264,6 @@ export class Terminal implements ITerminal {
|
||||
// Rename the progress bar so that the next hacks dont trigger it. Re-enable terminal
|
||||
$("#hack-progress-bar").attr("id", "old-hack-progress-bar");
|
||||
$("#hack-progress").attr("id", "old-hack-progress");
|
||||
this.resetTerminalInput();
|
||||
$("input[class=terminal-input]").prop("disabled", false);
|
||||
}
|
||||
|
||||
@ -320,16 +332,13 @@ export class Terminal implements ITerminal {
|
||||
return null;
|
||||
}
|
||||
|
||||
resetTerminalInput(): void {
|
||||
OldTerminal.resetTerminalInput();
|
||||
}
|
||||
|
||||
cwd(): string {
|
||||
return this.currDir;
|
||||
}
|
||||
|
||||
setcwd(dir: string): void {
|
||||
this.currDir = dir;
|
||||
this.hasChanges = true;
|
||||
}
|
||||
|
||||
async runContract(player: IPlayer, contractName: string): Promise<void> {
|
||||
@ -467,7 +476,6 @@ export class Terminal implements ITerminal {
|
||||
if (player.getCurrentServer().hostname == "darkweb") {
|
||||
checkIfConnectedToDarkweb(); // Posts a 'help' message if connecting to dark web
|
||||
}
|
||||
this.resetTerminalInput();
|
||||
}
|
||||
|
||||
executeCommands(engine: IEngine, player: IPlayer, commands: string): void {
|
||||
@ -490,6 +498,11 @@ export class Terminal implements ITerminal {
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.outputHistory = [{ text: `Bitburner v${CONSTANTS.Version}`, color: "primary" }];
|
||||
this.hasChanges = true;
|
||||
}
|
||||
|
||||
executeCommand(engine: IEngine, player: IPlayer, command: string): void {
|
||||
if (this.hackFlag || this.backdoorFlag || this.analyzeFlag) {
|
||||
this.error(`Cannot execute command (${command}) while an action is in progress`);
|
||||
@ -666,8 +679,8 @@ export class Terminal implements ITerminal {
|
||||
cat: cat,
|
||||
cd: cd,
|
||||
check: check,
|
||||
cls: clear,
|
||||
clear: clear,
|
||||
cls: () => this.clear(),
|
||||
clear: () => this.clear(),
|
||||
connect: connect,
|
||||
download: download,
|
||||
expr: expr,
|
||||
|
@ -41,8 +41,5 @@ export function cd(
|
||||
}
|
||||
|
||||
terminal.setcwd(evaledDir);
|
||||
|
||||
// Reset input to update current directory on UI
|
||||
terminal.resetTerminalInput();
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,4 @@ export function home(
|
||||
player.getCurrentServer().isConnectedTo = true;
|
||||
terminal.print("Connected to home");
|
||||
terminal.setcwd("/");
|
||||
terminal.resetTerminalInput();
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { post } from "../ui/postToTerminal";
|
||||
|
||||
import { containsAllStrings, longestCommonStart } from "../../utils/StringHelperFunctions";
|
||||
|
||||
/**
|
||||
@ -9,7 +7,12 @@ import { containsAllStrings, longestCommonStart } from "../../utils/StringHelper
|
||||
* @param arg {string} Last argument that is being completed
|
||||
* @param allPossibilities {string[]} All values that `arg` can complete to
|
||||
*/
|
||||
export function tabCompletion(command: string, arg: string, allPossibilities: string[]): void {
|
||||
export function tabCompletion(
|
||||
command: string,
|
||||
arg: string,
|
||||
allPossibilities: string[],
|
||||
oldValue: string,
|
||||
): string[] | string | undefined {
|
||||
if (!(allPossibilities.constructor === Array)) {
|
||||
return;
|
||||
}
|
||||
@ -33,14 +36,6 @@ export function tabCompletion(command: string, arg: string, allPossibilities: st
|
||||
}
|
||||
}
|
||||
|
||||
const textBoxElem = document.getElementById("terminal-input-text-box");
|
||||
if (textBoxElem == null) {
|
||||
console.warn(`Couldn't find terminal input DOM element (id=terminal-input-text-box) when trying to autocomplete`);
|
||||
return;
|
||||
}
|
||||
const textBox = textBoxElem as HTMLInputElement;
|
||||
|
||||
const oldValue = textBox.value;
|
||||
const semiColonIndex = oldValue.lastIndexOf(";");
|
||||
|
||||
let val = "";
|
||||
@ -56,13 +51,11 @@ export function tabCompletion(command: string, arg: string, allPossibilities: st
|
||||
|
||||
if (semiColonIndex === -1) {
|
||||
// No semicolon, so replace the whole command
|
||||
textBox.value = val;
|
||||
return val;
|
||||
} else {
|
||||
// Replace only after the last semicolon
|
||||
textBox.value = textBox.value.slice(0, semiColonIndex + 1) + " " + val;
|
||||
return oldValue.slice(0, semiColonIndex + 1) + " " + val;
|
||||
}
|
||||
|
||||
textBox.focus();
|
||||
} else {
|
||||
const longestStartSubstr = longestCommonStart(allPossibilities);
|
||||
/**
|
||||
@ -70,41 +63,30 @@ export function tabCompletion(command: string, arg: string, allPossibilities: st
|
||||
* as whatevers already in terminal, just list all possible options. Otherwise,
|
||||
* change the input in the terminal to the longest common starting substr
|
||||
*/
|
||||
let allOptionsStr = "";
|
||||
for (let i = 0; i < allPossibilities.length; ++i) {
|
||||
allOptionsStr += allPossibilities[i];
|
||||
allOptionsStr += " ";
|
||||
}
|
||||
if (arg === "") {
|
||||
if (longestStartSubstr === command) {
|
||||
post("> " + command);
|
||||
post(allOptionsStr);
|
||||
return allPossibilities;
|
||||
} else {
|
||||
if (semiColonIndex === -1) {
|
||||
// No semicolon, so replace the whole command
|
||||
textBox.value = longestStartSubstr;
|
||||
return longestStartSubstr;
|
||||
} else {
|
||||
// Replace only after the last semicolon
|
||||
textBox.value = `${textBox.value.slice(0, semiColonIndex + 1)} ${longestStartSubstr}`;
|
||||
return `${oldValue.slice(0, semiColonIndex + 1)} ${longestStartSubstr}`;
|
||||
}
|
||||
|
||||
textBox.focus();
|
||||
}
|
||||
} else {
|
||||
if (longestStartSubstr === arg) {
|
||||
// List all possible options
|
||||
post("> " + command + " " + arg);
|
||||
post(allOptionsStr);
|
||||
return allPossibilities;
|
||||
} else {
|
||||
if (semiColonIndex == -1) {
|
||||
// No semicolon, so replace the whole command
|
||||
textBox.value = `${command} ${longestStartSubstr}`;
|
||||
return `${command} ${longestStartSubstr}`;
|
||||
} else {
|
||||
// Replace only after the last semicolon
|
||||
textBox.value = `${textBox.value.slice(0, semiColonIndex + 1)} ${command} ${longestStartSubstr}`;
|
||||
return `${oldValue.slice(0, semiColonIndex + 1)} ${command} ${longestStartSubstr}`;
|
||||
}
|
||||
|
||||
textBox.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
371
src/Terminal/ui/TerminalRoot.tsx
Normal file
371
src/Terminal/ui/TerminalRoot.tsx
Normal file
@ -0,0 +1,371 @@
|
||||
import React, { useState, useEffect, useRef } from "react";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import { makeStyles, createStyles, Theme } from "@material-ui/core/styles";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import Box from "@material-ui/core/Box";
|
||||
import { KEY } from "../../../utils/helpers/keyCodes";
|
||||
import { ITerminal } from "../ITerminal";
|
||||
import { IEngine } from "../../IEngine";
|
||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||
import { determineAllPossibilitiesForTabCompletion } from "../determineAllPossibilitiesForTabCompletion";
|
||||
import { tabCompletion } from "../tabCompletion";
|
||||
import { FconfSettings } from "../../Fconf/FconfSettings";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
textfield: {
|
||||
margin: 0,
|
||||
width: "100%",
|
||||
},
|
||||
input: {
|
||||
backgroundColor: "#000",
|
||||
},
|
||||
nopadding: {
|
||||
padding: 0,
|
||||
},
|
||||
preformatted: {
|
||||
whiteSpace: "pre",
|
||||
margin: 0,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
interface IProps {
|
||||
terminal: ITerminal;
|
||||
engine: IEngine;
|
||||
player: IPlayer;
|
||||
}
|
||||
|
||||
export function TerminalRoot({ terminal, engine, player }: IProps): React.ReactElement {
|
||||
const terminalInput = useRef<HTMLInputElement>(null);
|
||||
const setRerender = useState(false)[1];
|
||||
function rerender(): void {
|
||||
setRerender((old) => !old);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const id = setInterval(() => {
|
||||
if (terminal.pollChanges()) rerender();
|
||||
}, 100);
|
||||
return () => clearInterval(id);
|
||||
}, []);
|
||||
|
||||
const [value, setValue] = useState("");
|
||||
const [possibilities, setPossibilities] = useState<string[]>([]);
|
||||
const classes = useStyles();
|
||||
|
||||
function handleValueChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||
setValue(event.target.value);
|
||||
setPossibilities([]);
|
||||
}
|
||||
|
||||
function modifyInput(mod: string): void {
|
||||
const ref = terminalInput.current;
|
||||
if (!ref) return;
|
||||
const inputLength = value.length;
|
||||
const start = ref.selectionStart;
|
||||
if (start === null) return;
|
||||
const inputText = ref.value;
|
||||
|
||||
switch (mod.toLowerCase()) {
|
||||
case "backspace":
|
||||
if (start > 0 && start <= inputLength + 1) {
|
||||
setValue(inputText.substr(0, start - 1) + inputText.substr(start));
|
||||
}
|
||||
break;
|
||||
case "deletewordbefore": // Delete rest of word before the cursor
|
||||
for (var delStart = start - 1; delStart > 0; --delStart) {
|
||||
if (inputText.charAt(delStart) === " ") {
|
||||
setValue(inputText.substr(0, delStart) + inputText.substr(start));
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "deletewordafter": // Delete rest of word after the cursor
|
||||
for (var delStart = start + 1; delStart <= value.length + 1; ++delStart) {
|
||||
if (inputText.charAt(delStart) === " ") {
|
||||
setValue(inputText.substr(0, start) + inputText.substr(delStart));
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "clearafter": // Deletes everything after cursor
|
||||
break;
|
||||
case "clearbefore:": // Deleetes everything before cursor
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function moveTextCursor(loc: string) {
|
||||
const ref = terminalInput.current;
|
||||
if (!ref) return;
|
||||
const inputLength = value.length;
|
||||
const start = ref.selectionStart;
|
||||
if (start === null) return;
|
||||
|
||||
switch (loc.toLowerCase()) {
|
||||
case "home":
|
||||
ref.setSelectionRange(0, 0);
|
||||
break;
|
||||
case "end":
|
||||
ref.setSelectionRange(inputLength, inputLength);
|
||||
break;
|
||||
case "prevchar":
|
||||
if (start > 0) {
|
||||
ref.setSelectionRange(start - 1, start - 1);
|
||||
}
|
||||
break;
|
||||
case "prevword":
|
||||
for (let i = start - 2; i >= 0; --i) {
|
||||
if (ref.value.charAt(i) === " ") {
|
||||
ref.setSelectionRange(i + 1, i + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ref.setSelectionRange(0, 0);
|
||||
break;
|
||||
case "nextchar":
|
||||
ref.setSelectionRange(start + 1, start + 1);
|
||||
break;
|
||||
case "nextword":
|
||||
for (let i = start + 1; i <= inputLength; ++i) {
|
||||
if (ref.value.charAt(i) === " ") {
|
||||
ref.setSelectionRange(i, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ref.setSelectionRange(inputLength, inputLength);
|
||||
break;
|
||||
default:
|
||||
console.warn("Invalid loc argument in Terminal.moveTextCursor()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Catch all key inputs and redirect them to the terminal.
|
||||
useEffect(() => {
|
||||
function keyDown(this: Document, event: KeyboardEvent): void {
|
||||
if (terminal.contractOpen) return;
|
||||
const ref = terminalInput.current;
|
||||
if (ref) ref.focus();
|
||||
}
|
||||
document.addEventListener("keydown", keyDown);
|
||||
return () => document.removeEventListener("keydown", keyDown);
|
||||
});
|
||||
|
||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||
// Run command.
|
||||
if (event.keyCode === KEY.ENTER && value !== "") {
|
||||
event.preventDefault();
|
||||
terminal.executeCommands(engine, player, value);
|
||||
setValue("");
|
||||
return;
|
||||
}
|
||||
|
||||
// Autocomplete
|
||||
if (event.keyCode === KEY.TAB && value !== "") {
|
||||
event.preventDefault();
|
||||
|
||||
let copy = value;
|
||||
const semiColonIndex = copy.lastIndexOf(";");
|
||||
if (semiColonIndex !== -1) {
|
||||
copy = copy.slice(semiColonIndex + 1);
|
||||
}
|
||||
|
||||
copy = copy.trim();
|
||||
copy = copy.replace(/\s\s+/g, " ");
|
||||
|
||||
const commandArray = copy.split(" ");
|
||||
let index = commandArray.length - 2;
|
||||
if (index < -1) {
|
||||
index = 0;
|
||||
}
|
||||
const allPos = determineAllPossibilitiesForTabCompletion(player, copy, index, terminal.cwd());
|
||||
if (allPos.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let arg = "";
|
||||
let command = "";
|
||||
if (commandArray.length == 0) {
|
||||
return;
|
||||
}
|
||||
if (commandArray.length == 1) {
|
||||
command = commandArray[0];
|
||||
} else if (commandArray.length == 2) {
|
||||
command = commandArray[0];
|
||||
arg = commandArray[1];
|
||||
} else if (commandArray.length == 3) {
|
||||
command = commandArray[0] + " " + commandArray[1];
|
||||
arg = commandArray[2];
|
||||
} else {
|
||||
arg = commandArray.pop() + "";
|
||||
command = commandArray.join(" ");
|
||||
}
|
||||
|
||||
const newValue = tabCompletion(command, arg, allPos, value);
|
||||
if (typeof newValue === "string" && newValue !== "") {
|
||||
setValue(newValue);
|
||||
}
|
||||
if (Array.isArray(newValue)) {
|
||||
setPossibilities(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear screen.
|
||||
if (event.keyCode === KEY.L && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
terminal.clear();
|
||||
rerender();
|
||||
}
|
||||
|
||||
// Select previous command.
|
||||
if (
|
||||
event.keyCode === KEY.UPARROW ||
|
||||
(FconfSettings.ENABLE_BASH_HOTKEYS && event.keyCode === KEY.P && event.ctrlKey)
|
||||
) {
|
||||
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const i = terminal.commandHistoryIndex;
|
||||
const len = terminal.commandHistory.length;
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
if (i < 0 || i > len) {
|
||||
terminal.commandHistoryIndex = len;
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
--terminal.commandHistoryIndex;
|
||||
}
|
||||
const prevCommand = terminal.commandHistory[terminal.commandHistoryIndex];
|
||||
setValue(prevCommand);
|
||||
const ref = terminalInput.current;
|
||||
if (ref) {
|
||||
setTimeout(function () {
|
||||
ref.selectionStart = ref.selectionEnd = 10000;
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
// Select next command
|
||||
if (
|
||||
event.keyCode === KEY.DOWNARROW ||
|
||||
(FconfSettings.ENABLE_BASH_HOTKEYS && event.keyCode === KEY.M && event.ctrlKey)
|
||||
) {
|
||||
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const i = terminal.commandHistoryIndex;
|
||||
const len = terminal.commandHistory.length;
|
||||
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
if (i < 0 || i > len) {
|
||||
terminal.commandHistoryIndex = len;
|
||||
}
|
||||
|
||||
// Latest command, put nothing
|
||||
if (i == len || i == len - 1) {
|
||||
terminal.commandHistoryIndex = len;
|
||||
setValue("");
|
||||
} else {
|
||||
++terminal.commandHistoryIndex;
|
||||
const prevCommand = terminal.commandHistory[terminal.commandHistoryIndex];
|
||||
setValue(prevCommand);
|
||||
}
|
||||
}
|
||||
|
||||
// Extra Bash Emulation Hotkeys, must be enabled through .fconf
|
||||
if (FconfSettings.ENABLE_BASH_HOTKEYS) {
|
||||
if (event.keyCode === KEY.A && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
moveTextCursor("home");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.E && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
moveTextCursor("end");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.B && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
moveTextCursor("prevchar");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.B && event.altKey) {
|
||||
event.preventDefault();
|
||||
moveTextCursor("prevword");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.F && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
moveTextCursor("nextchar");
|
||||
}
|
||||
|
||||
if (event.keyCode === KEY.F && event.altKey) {
|
||||
event.preventDefault();
|
||||
moveTextCursor("nextword");
|
||||
}
|
||||
|
||||
if ((event.keyCode === KEY.H || event.keyCode === KEY.D) && event.ctrlKey) {
|
||||
modifyInput("backspace");
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// TODO AFTER THIS:
|
||||
// alt + d deletes word after cursor
|
||||
// ^w deletes word before cursor
|
||||
// ^k clears line after cursor
|
||||
// ^u clears line before cursor
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Box position="fixed" bottom="0" width="100%">
|
||||
<List classes={{ root: classes.nopadding }}>
|
||||
{terminal.outputHistory.map((output, i) => (
|
||||
<ListItem key={i} classes={{ root: classes.nopadding }}>
|
||||
<Typography classes={{ root: classes.preformatted }} color={output.color} paragraph={false}>
|
||||
{output.text}
|
||||
</Typography>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
{possibilities.length > 0 && (
|
||||
<>
|
||||
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
|
||||
Possible autocomplete candidate:
|
||||
</Typography>
|
||||
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
|
||||
{possibilities.join(" ")}
|
||||
</Typography>
|
||||
</>
|
||||
)}
|
||||
<TextField
|
||||
autoFocus
|
||||
classes={{ root: classes.textfield }}
|
||||
value={value}
|
||||
onChange={handleValueChange}
|
||||
inputRef={terminalInput}
|
||||
InputProps={{
|
||||
className: classes.input,
|
||||
startAdornment: (
|
||||
<>
|
||||
<Typography color="primary">
|
||||
[{player.getCurrentServer().hostname} ~{terminal.cwd()}]>
|
||||
</Typography>
|
||||
</>
|
||||
),
|
||||
spellCheck: false,
|
||||
onKeyDown: onKeyDown,
|
||||
}}
|
||||
></TextField>
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -54,7 +54,8 @@ import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
|
||||
import { initSpecialServerIps } from "./Server/SpecialServerIps";
|
||||
import { initSymbolToStockMap, processStockPrices, displayStockMarketContent } from "./StockMarket/StockMarket";
|
||||
import { MilestonesRoot } from "./Milestones/ui/MilestonesRoot";
|
||||
import { Terminal, postVersion } from "./Terminal";
|
||||
import { TerminalRoot } from "./Terminal/ui/TerminalRoot";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { TutorialRoot } from "./Tutorial/ui/TutorialRoot";
|
||||
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
|
||||
|
||||
@ -90,7 +91,6 @@ const Engine = {
|
||||
// Generic page that most react loads into.
|
||||
content: null,
|
||||
// Main menu content
|
||||
terminalContent: null,
|
||||
infiltrationContent: null,
|
||||
workInProgressContent: null,
|
||||
redPillContent: null,
|
||||
@ -107,10 +107,15 @@ const Engine = {
|
||||
|
||||
loadTerminalContent: function () {
|
||||
Engine.hideAllContent();
|
||||
Engine.Display.terminalContent.style.display = "block";
|
||||
document.getElementById("terminal-input-td").scrollIntoView(false);
|
||||
routing.navigateTo(Page.Terminal);
|
||||
MainMenuLinks.Terminal.classList.add("active");
|
||||
Engine.Display.content.style.display = "block";
|
||||
routing.navigateTo(Page.CharacterInfo);
|
||||
ReactDOM.render(
|
||||
<Theme>
|
||||
<TerminalRoot terminal={Terminal} engine={this} player={Player} />
|
||||
</Theme>,
|
||||
Engine.Display.content,
|
||||
);
|
||||
MainMenuLinks.Stats.classList.add("active");
|
||||
},
|
||||
|
||||
loadCharacterContent: function () {
|
||||
@ -382,8 +387,6 @@ const Engine = {
|
||||
|
||||
// Helper function that hides all content
|
||||
hideAllContent: function () {
|
||||
Engine.Display.terminalContent.style.display = "none";
|
||||
|
||||
Engine.Display.content.style.display = "none";
|
||||
Engine.Display.content.scrollTop = 0;
|
||||
ReactDOM.unmountComponentAtNode(Engine.Display.content);
|
||||
@ -844,16 +847,12 @@ const Engine = {
|
||||
}
|
||||
|
||||
ReactDOM.render(<SidebarRoot engine={this} player={Player} />, document.getElementById("sidebar"));
|
||||
// Initialize labels on game settings
|
||||
Terminal.resetTerminalInput();
|
||||
Engine.loadTerminalContent();
|
||||
},
|
||||
|
||||
setDisplayElements: function () {
|
||||
Engine.Display.content = document.getElementById("generic-react-container");
|
||||
Engine.Display.content.style.display = "none";
|
||||
// Content elements
|
||||
Engine.Display.terminalContent = document.getElementById("terminal-container");
|
||||
routing.navigateTo(Page.Terminal);
|
||||
|
||||
Engine.Display.missionContent = document.getElementById("mission-container");
|
||||
Engine.Display.missionContent.style.display = "none";
|
||||
@ -878,9 +877,6 @@ const Engine = {
|
||||
|
||||
// Initialization
|
||||
init: function () {
|
||||
// Message at the top of terminal
|
||||
postVersion();
|
||||
|
||||
// Player was working cancel button
|
||||
if (Player.isWorking) {
|
||||
var cancelButton = document.getElementById("work-in-progress-cancel-button");
|
||||
|
@ -42,25 +42,6 @@
|
||||
<div id="mainmenu-container" style="display: flex; flex-direction: row">
|
||||
<!-- Main menu -->
|
||||
<div id="sidebar" style=""></div>
|
||||
<!-- Terminal page -->
|
||||
<div id="terminal-container" style="flex-grow: 1">
|
||||
<table id="terminal">
|
||||
<tr id="terminal-input">
|
||||
<td id="terminal-input-td" tabindex="2">
|
||||
$
|
||||
<input
|
||||
type="text"
|
||||
id="terminal-input-text-box"
|
||||
class="terminal-input"
|
||||
tabindex="1"
|
||||
onfocus="this.value = this.value;"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="generic-menupage-container">
|
||||
<div id="generic-react-container"></div>
|
||||
</div>
|
||||
|
@ -1,18 +1,6 @@
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { getElementById } from "../../utils/uiHelpers/getElementById";
|
||||
|
||||
/**
|
||||
* Adds some output to the terminal.
|
||||
* @param input Text or HTML to output to the terminal
|
||||
*/
|
||||
export function post(input: string): void {
|
||||
postContent(input);
|
||||
}
|
||||
|
||||
export function postError(input: string): void {
|
||||
postContent(`ERROR: ${input}`, { color: "#ff2929" });
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds some output to the terminal with an identifier of "hack-progress-bar"
|
||||
* @param input Text or HTML to output to the terminal
|
||||
@ -34,10 +22,6 @@ interface IPostContentConfig {
|
||||
color?: string; // Additional class for terminal-line. Does NOT replace
|
||||
}
|
||||
|
||||
export function postElement(element: JSX.Element): void {
|
||||
postContent(renderToStaticMarkup(element));
|
||||
}
|
||||
|
||||
export function postContent(input: string, config: IPostContentConfig = {}): void {
|
||||
// tslint:disable-next-line:max-line-length
|
||||
const style = `color: ${
|
||||
@ -49,13 +33,4 @@ export function postContent(input: string, config: IPostContentConfig = {}): voi
|
||||
} style="${style}">${input}</td></tr>`;
|
||||
const inputElement: HTMLElement = getElementById("terminal-input");
|
||||
inputElement.insertAdjacentHTML("beforebegin", content);
|
||||
scrollTerminalToBottom();
|
||||
}
|
||||
|
||||
function scrollTerminalToBottom(): void {
|
||||
const container: HTMLElement = getElementById("terminal-container");
|
||||
container.scrollTop = container.scrollHeight;
|
||||
const td = document.getElementById("terminal-input-td");
|
||||
if (td === null) throw new Error("terminal-input-td should not be null");
|
||||
td.scrollIntoView(false);
|
||||
}
|
||||
|
Reference in New Issue
Block a user