mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 12:15:44 +01:00
UI: LogBox overhaul (#508)
This commit is contained in:
parent
113af6e711
commit
4503da6226
@ -84,6 +84,7 @@
|
||||
| [Player](./bitburner.player.md) | |
|
||||
| [ProcessInfo](./bitburner.processinfo.md) | A single process on a server. |
|
||||
| [Product](./bitburner.product.md) | Product in a warehouse |
|
||||
| [ReactElement](./bitburner.reactelement.md) | A stand-in for the real React.ReactElement, which API-extractor doesn't know about. Don't try to create one of these by hand; use React.createElement(). |
|
||||
| [RecentScript](./bitburner.recentscript.md) | |
|
||||
| [ReputationFormulas](./bitburner.reputationformulas.md) | Reputation formulas |
|
||||
| [ResetInfo](./bitburner.resetinfo.md) | Various info about resets |
|
||||
@ -99,6 +100,7 @@
|
||||
| [Stanek](./bitburner.stanek.md) | Stanek's Gift API. |
|
||||
| [StockOrder](./bitburner.stockorder.md) | <p>Return value of [getOrders](./bitburner.tix.getorders.md)</p><p>Keys are stock symbols, properties are arrays of [StockOrderObject](./bitburner.stockorderobject.md)</p> |
|
||||
| [StockOrderObject](./bitburner.stockorderobject.md) | Value in map of [StockOrder](./bitburner.stockorder.md) |
|
||||
| [TailProperties](./bitburner.tailproperties.md) | |
|
||||
| [TIX](./bitburner.tix.md) | Stock market API |
|
||||
| [UserInterface](./bitburner.userinterface.md) | User Interface API. |
|
||||
| [UserInterfaceTheme](./bitburner.userinterfacetheme.md) | Interface Theme |
|
||||
|
@ -21,7 +21,7 @@ exec(
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| script | string | Filename of script to execute. |
|
||||
| script | string | Filename of script to execute. This file must already exist on the target server. |
|
||||
| hostname | string | Hostname of the <code>target server</code> on which to execute the script. |
|
||||
| threadOrOptions | number \| [RunOptions](./bitburner.runoptions.md) | _(Optional)_ Either an integer number of threads for new script, or a [RunOptions](./bitburner.runoptions.md) object. Threads defaults to 1. |
|
||||
| args | (string \| number \| boolean)\[\] | Additional arguments to pass into the new script that is being run. Note that if any arguments are being passed into the new script, then the third argument threadOrOptions must be filled in with a value. |
|
||||
@ -36,7 +36,7 @@ Returns the PID of a successfully started script, and 0 otherwise.
|
||||
|
||||
RAM cost: 1.3 GB
|
||||
|
||||
Run a script as a separate process on a specified server. This is similar to the function [run](./bitburner.ns.run.md) except that it can be used to run a script on any server, instead of just the current server.
|
||||
Run a script as a separate process on a specified server. This is similar to the function [run](./bitburner.ns.run.md) except that it can be used to run a script that already exists on any server, instead of just the current server.
|
||||
|
||||
If the script was successfully started, then this function returns the PID of that script. Otherwise, it returns 0.
|
||||
|
||||
|
@ -152,6 +152,7 @@ export async function main(ns) {
|
||||
| [scriptKill(script, host)](./bitburner.ns.scriptkill.md) | Kill all scripts with a filename. |
|
||||
| [scriptRunning(script, host)](./bitburner.ns.scriptrunning.md) | Check if any script with a filename is running. |
|
||||
| [serverExists(host)](./bitburner.ns.serverexists.md) | Returns a boolean denoting whether or not the specified server exists. |
|
||||
| [setTitle(title, pid)](./bitburner.ns.settitle.md) | Set the title of the tail window of a script. |
|
||||
| [share()](./bitburner.ns.share.md) | Share the server's ram with your factions. |
|
||||
| [sleep(millis)](./bitburner.ns.sleep.md) | Suspends the script for n milliseconds. |
|
||||
| [spawn(script, threadOrOptions, args)](./bitburner.ns.spawn.md) | Terminate current script and start another in 10 seconds. |
|
||||
|
39
markdown/bitburner.ns.settitle.md
Normal file
39
markdown/bitburner.ns.settitle.md
Normal file
@ -0,0 +1,39 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [NS](./bitburner.ns.md) > [setTitle](./bitburner.ns.settitle.md)
|
||||
|
||||
## NS.setTitle() method
|
||||
|
||||
Set the title of the tail window of a script.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
setTitle(title: string | ReactElement, pid?: number): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| title | string \| [ReactElement](./bitburner.reactelement.md) | |
|
||||
| pid | number | _(Optional)_ Optional. PID of the script having its tail closed. If omitted, the current script is used. |
|
||||
|
||||
**Returns:**
|
||||
|
||||
void
|
||||
|
||||
## Remarks
|
||||
|
||||
RAM cost: 0 GB
|
||||
|
||||
This sets the title to the given string, and also forces an update of the tail window's contents.
|
||||
|
||||
The title is saved across restarts, but only if it is a simple string.
|
||||
|
||||
If the pid is unspecified, it will modify the current script’s logs.
|
||||
|
||||
Otherwise, the pid argument can be used to change the logs from another script.
|
||||
|
||||
It is possible to pass a React Element instead of a string. Get these by calling React.createElement() with appropriate parameters. You should either know or be willing to learn about the React UI library if you go down this route, and there will likely be rough edges.
|
||||
|
11
markdown/bitburner.reactelement.key.md
Normal file
11
markdown/bitburner.reactelement.key.md
Normal file
@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [ReactElement](./bitburner.reactelement.md) > [key](./bitburner.reactelement.key.md)
|
||||
|
||||
## ReactElement.key property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
key: string | number | null;
|
||||
```
|
22
markdown/bitburner.reactelement.md
Normal file
22
markdown/bitburner.reactelement.md
Normal file
@ -0,0 +1,22 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [ReactElement](./bitburner.reactelement.md)
|
||||
|
||||
## ReactElement interface
|
||||
|
||||
A stand-in for the real React.ReactElement, which API-extractor doesn't know about. Don't try to create one of these by hand; use React.createElement().
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
interface ReactElement
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [key](./bitburner.reactelement.key.md) | | string \| number \| null | |
|
||||
| [props](./bitburner.reactelement.props.md) | | any | |
|
||||
| [type](./bitburner.reactelement.type.md) | | string \| ((props: any) => [ReactElement](./bitburner.reactelement.md) \| null) \| (new (props: any) => object) | |
|
||||
|
11
markdown/bitburner.reactelement.props.md
Normal file
11
markdown/bitburner.reactelement.props.md
Normal file
@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [ReactElement](./bitburner.reactelement.md) > [props](./bitburner.reactelement.props.md)
|
||||
|
||||
## ReactElement.props property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
props: any;
|
||||
```
|
11
markdown/bitburner.reactelement.type.md
Normal file
11
markdown/bitburner.reactelement.type.md
Normal file
@ -0,0 +1,11 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [ReactElement](./bitburner.reactelement.md) > [type](./bitburner.reactelement.type.md)
|
||||
|
||||
## ReactElement.type property
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
type: string | ((props: any) => ReactElement | null) | (new (props: any) => object);
|
||||
```
|
@ -27,6 +27,8 @@ interface RunningScript
|
||||
| [pid](./bitburner.runningscript.pid.md) | | number | Process ID. Must be an integer |
|
||||
| [ramUsage](./bitburner.runningscript.ramusage.md) | | number | How much RAM this script uses for ONE thread |
|
||||
| [server](./bitburner.runningscript.server.md) | | string | Hostname of the server on which this script runs |
|
||||
| [tailProperties](./bitburner.runningscript.tailproperties.md) | | [TailProperties](./bitburner.tailproperties.md) \| null | Properties of the tail window, or null if it is not shown |
|
||||
| [temporary](./bitburner.runningscript.temporary.md) | | boolean | Whether this RunningScript is excluded from saves |
|
||||
| [threads](./bitburner.runningscript.threads.md) | | number | Number of threads that this script runs with |
|
||||
| [title](./bitburner.runningscript.title.md) | | string \| [ReactElement](./bitburner.reactelement.md) | The title, as shown in the script's log box. Defaults to the name + args, but can be changed by the user. If it is set to a React element (only by the user), that will not be persisted, and will be restored to default on load. |
|
||||
|
||||
|
13
markdown/bitburner.runningscript.tailproperties.md
Normal file
13
markdown/bitburner.runningscript.tailproperties.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [RunningScript](./bitburner.runningscript.md) > [tailProperties](./bitburner.runningscript.tailproperties.md)
|
||||
|
||||
## RunningScript.tailProperties property
|
||||
|
||||
Properties of the tail window, or null if it is not shown
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
tailProperties: TailProperties | null;
|
||||
```
|
13
markdown/bitburner.runningscript.title.md
Normal file
13
markdown/bitburner.runningscript.title.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [RunningScript](./bitburner.runningscript.md) > [title](./bitburner.runningscript.title.md)
|
||||
|
||||
## RunningScript.title property
|
||||
|
||||
The title, as shown in the script's log box. Defaults to the name + args, but can be changed by the user. If it is set to a React element (only by the user), that will not be persisted, and will be restored to default on load.
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
title: string | ReactElement;
|
||||
```
|
13
markdown/bitburner.tailproperties.height.md
Normal file
13
markdown/bitburner.tailproperties.height.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [TailProperties](./bitburner.tailproperties.md) > [height](./bitburner.tailproperties.height.md)
|
||||
|
||||
## TailProperties.height property
|
||||
|
||||
Height of the log window content area
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
height: number;
|
||||
```
|
22
markdown/bitburner.tailproperties.md
Normal file
22
markdown/bitburner.tailproperties.md
Normal file
@ -0,0 +1,22 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [TailProperties](./bitburner.tailproperties.md)
|
||||
|
||||
## TailProperties interface
|
||||
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
interface TailProperties
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Modifiers | Type | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| [height](./bitburner.tailproperties.height.md) | | number | Height of the log window content area |
|
||||
| [width](./bitburner.tailproperties.width.md) | | number | Width of the log window content area |
|
||||
| [x](./bitburner.tailproperties.x.md) | | number | X-coordinate of the log window |
|
||||
| [y](./bitburner.tailproperties.y.md) | | number | Y-coordinate of the log window |
|
||||
|
13
markdown/bitburner.tailproperties.width.md
Normal file
13
markdown/bitburner.tailproperties.width.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [TailProperties](./bitburner.tailproperties.md) > [width](./bitburner.tailproperties.width.md)
|
||||
|
||||
## TailProperties.width property
|
||||
|
||||
Width of the log window content area
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
width: number;
|
||||
```
|
13
markdown/bitburner.tailproperties.x.md
Normal file
13
markdown/bitburner.tailproperties.x.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [TailProperties](./bitburner.tailproperties.md) > [x](./bitburner.tailproperties.x.md)
|
||||
|
||||
## TailProperties.x property
|
||||
|
||||
X-coordinate of the log window
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
x: number;
|
||||
```
|
13
markdown/bitburner.tailproperties.y.md
Normal file
13
markdown/bitburner.tailproperties.y.md
Normal file
@ -0,0 +1,13 @@
|
||||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [bitburner](./bitburner.md) > [TailProperties](./bitburner.tailproperties.md) > [y](./bitburner.tailproperties.y.md)
|
||||
|
||||
## TailProperties.y property
|
||||
|
||||
Y-coordinate of the log window
|
||||
|
||||
**Signature:**
|
||||
|
||||
```typescript
|
||||
y: number;
|
||||
```
|
@ -236,6 +236,7 @@ v2.3.1 dev
|
||||
|
||||
GENERAL / MISC:
|
||||
|
||||
* Tail window overhaul, ability to set tail title with ns.setTitle, other tail bugfixes and improvements. (@d0sboots)
|
||||
* Nerf noodle bar
|
||||
`,
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import React from "react";
|
||||
import { NetscriptContext } from "./APIWrapper";
|
||||
import { WorkerScript } from "./WorkerScript";
|
||||
import { killWorkerScript } from "./killWorkerScript";
|
||||
@ -38,6 +39,7 @@ import { isPositiveInteger, PositiveInteger, Unknownify } from "../types";
|
||||
import { Engine } from "../engine";
|
||||
import { resolveFilePath, FilePath } from "../Paths/FilePath";
|
||||
import { hasScriptExtension, ScriptFilePath } from "../Paths/ScriptFilePath";
|
||||
import { CustomBoundary } from "../ui/Components/CustomBoundary";
|
||||
|
||||
export const helpers = {
|
||||
string,
|
||||
@ -736,6 +738,7 @@ function getCannotFindRunningScriptErrorMessage(ident: ScriptIdentifier): string
|
||||
* @returns A sanitized, NS-facing copy of the RunningScript
|
||||
*/
|
||||
function createPublicRunningScript(runningScript: RunningScript): IRunningScript {
|
||||
const logProps = runningScript.tailProps;
|
||||
return {
|
||||
args: runningScript.args.slice(),
|
||||
filename: runningScript.filename,
|
||||
@ -749,6 +752,16 @@ function createPublicRunningScript(runningScript: RunningScript): IRunningScript
|
||||
pid: runningScript.pid,
|
||||
ramUsage: runningScript.ramUsage,
|
||||
server: runningScript.server,
|
||||
tailProperties:
|
||||
!logProps || !logProps.isVisible()
|
||||
? null
|
||||
: {
|
||||
x: logProps.x,
|
||||
y: logProps.y,
|
||||
width: logProps.width,
|
||||
height: logProps.height,
|
||||
},
|
||||
title: runningScript.title,
|
||||
threads: runningScript.threads,
|
||||
temporary: runningScript.temporary,
|
||||
};
|
||||
@ -800,3 +813,14 @@ export function handleUnknownError(e: unknown, ws: WorkerScript | ScriptDeath |
|
||||
}
|
||||
dialogBoxCreate(initialText + e);
|
||||
}
|
||||
|
||||
// Incrementing value for custom element keys
|
||||
let customElementKey = 0;
|
||||
|
||||
/**
|
||||
* Wrap a user-provided React Element (or maybe invalid junk) in an Error-catching component,
|
||||
* so the game won't crash and the user gets sensible messages.
|
||||
*/
|
||||
export function wrapUserNode(value: unknown) {
|
||||
return <CustomBoundary key={`PlayerContent${customElementKey++}`} children={value as React.ReactNode} />;
|
||||
}
|
@ -540,6 +540,7 @@ export const RamCosts: RamCostTree<NSFull> = {
|
||||
moveTail: 0,
|
||||
resizeTail: 0,
|
||||
closeTail: 0,
|
||||
setTitle: 0,
|
||||
clearPort: 0,
|
||||
openDevMenu: 0,
|
||||
alert: 0,
|
||||
|
@ -37,7 +37,7 @@ import { runScriptFromScript } from "./NetscriptWorker";
|
||||
import { killWorkerScript, killWorkerScriptByPid } from "./Netscript/killWorkerScript";
|
||||
import { workerScripts } from "./Netscript/WorkerScripts";
|
||||
import { WorkerScript } from "./Netscript/WorkerScript";
|
||||
import { helpers, assertObjectType } from "./Netscript/NetscriptHelpers";
|
||||
import { helpers, assertObjectType, wrapUserNode } from "./Netscript/NetscriptHelpers";
|
||||
import {
|
||||
formatExp,
|
||||
formatNumberNoSuffix,
|
||||
@ -49,7 +49,7 @@ import {
|
||||
formatNumber,
|
||||
} from "./ui/formatNumber";
|
||||
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
|
||||
import { LogBoxEvents, LogBoxCloserEvents, LogBoxPositionEvents, LogBoxSizeEvents } from "./ui/React/LogBoxManager";
|
||||
import { LogBoxEvents, LogBoxCloserEvents } from "./ui/React/LogBoxManager";
|
||||
import { arrayToString } from "./utils/helpers/ArrayHelpers";
|
||||
import { NetscriptGang } from "./NetscriptFunctions/Gang";
|
||||
import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
|
||||
@ -557,7 +557,12 @@ export const ns: InternalAPI<NSFull> = {
|
||||
const x = helpers.number(ctx, "x", _x);
|
||||
const y = helpers.number(ctx, "y", _y);
|
||||
const pid = helpers.number(ctx, "pid", _pid);
|
||||
LogBoxPositionEvents.emit({ pid, data: { x, y } });
|
||||
const runningScriptObj = helpers.getRunningScript(ctx, pid);
|
||||
if (runningScriptObj == null) {
|
||||
helpers.log(ctx, () => helpers.getCannotFindRunningScriptErrorMessage(pid));
|
||||
return;
|
||||
}
|
||||
runningScriptObj.tailProps?.setPosition(x, y);
|
||||
},
|
||||
resizeTail:
|
||||
(ctx) =>
|
||||
@ -565,7 +570,12 @@ export const ns: InternalAPI<NSFull> = {
|
||||
const w = helpers.number(ctx, "w", _w);
|
||||
const h = helpers.number(ctx, "h", _h);
|
||||
const pid = helpers.number(ctx, "pid", _pid);
|
||||
LogBoxSizeEvents.emit({ pid, data: { w, h } });
|
||||
const runningScriptObj = helpers.getRunningScript(ctx, pid);
|
||||
if (runningScriptObj == null) {
|
||||
helpers.log(ctx, () => helpers.getCannotFindRunningScriptErrorMessage(pid));
|
||||
return;
|
||||
}
|
||||
runningScriptObj.tailProps?.setSize(w, h);
|
||||
},
|
||||
closeTail:
|
||||
(ctx) =>
|
||||
@ -574,6 +584,18 @@ export const ns: InternalAPI<NSFull> = {
|
||||
//Emit an event to tell the game to close the tail window if it exists
|
||||
LogBoxCloserEvents.emit(pid);
|
||||
},
|
||||
setTitle:
|
||||
(ctx) =>
|
||||
(title, _pid = ctx.workerScript.scriptRef.pid) => {
|
||||
const pid = helpers.number(ctx, "pid", _pid);
|
||||
const runningScriptObj = helpers.getRunningScript(ctx, pid);
|
||||
if (runningScriptObj == null) {
|
||||
helpers.log(ctx, () => helpers.getCannotFindRunningScriptErrorMessage(pid));
|
||||
return;
|
||||
}
|
||||
runningScriptObj.title = typeof title === "string" ? title : wrapUserNode(title);
|
||||
runningScriptObj.tailProps?.rerender();
|
||||
},
|
||||
nuke: (ctx) => (_hostname) => {
|
||||
const hostname = helpers.string(ctx, "hostname", _hostname);
|
||||
|
||||
|
@ -1,16 +1,11 @@
|
||||
import React from "react";
|
||||
import { Player } from "../Player";
|
||||
import { Exploit } from "../Exploits/Exploit";
|
||||
import * as bcrypt from "bcryptjs";
|
||||
import { Apr1Events as devMenu } from "../ui/Apr1";
|
||||
import { InternalAPI } from "../Netscript/APIWrapper";
|
||||
import { helpers } from "../Netscript/NetscriptHelpers";
|
||||
import { helpers, wrapUserNode } from "../Netscript/NetscriptHelpers";
|
||||
import { Terminal } from "../Terminal";
|
||||
import { RamCostConstants } from "../Netscript/RamCostGenerator";
|
||||
import { CustomBoundary } from "../ui/Components/CustomBoundary";
|
||||
|
||||
// Incrementing value for custom element keys
|
||||
let customElementKey = 0;
|
||||
|
||||
export interface INetscriptExtra {
|
||||
heart: {
|
||||
@ -72,14 +67,10 @@ export function NetscriptExtra(): InternalAPI<INetscriptExtra> {
|
||||
return true;
|
||||
},
|
||||
tprintRaw: () => (value) => {
|
||||
Terminal.printRaw(
|
||||
<CustomBoundary key={`PlayerContent${customElementKey++}`} children={value as React.ReactNode} />,
|
||||
);
|
||||
Terminal.printRaw(wrapUserNode(value));
|
||||
},
|
||||
printRaw: (ctx) => (value) => {
|
||||
ctx.workerScript.print(
|
||||
<CustomBoundary key={`PlayerContent${customElementKey++}`} children={value as React.ReactNode} />,
|
||||
);
|
||||
ctx.workerScript.print(wrapUserNode(value));
|
||||
},
|
||||
};
|
||||
}
|
@ -17,6 +17,8 @@ import { getKeyList } from "../utils/helpers/getKeyList";
|
||||
import { ScriptFilePath } from "../Paths/ScriptFilePath";
|
||||
import { ScriptKey, scriptKey } from "../utils/helpers/scriptKey";
|
||||
|
||||
import type { LogBoxProperties } from "../ui/React/LogBoxManager";
|
||||
|
||||
export class RunningScript {
|
||||
// Script arguments
|
||||
args: ScriptArg[] = [];
|
||||
@ -65,6 +67,14 @@ export class RunningScript {
|
||||
// Cached key for ByArgs lookups. Will be overwritten by a correct ScriptKey in fromJSON or constructor
|
||||
scriptKey = "" as ScriptKey;
|
||||
|
||||
// Access to properties of the tail window. Can be used to get/set size, position, etc.
|
||||
tailProps = null as LogBoxProperties | null;
|
||||
|
||||
// The title, as shown in the script's log box. Defaults to the name + args,
|
||||
// but can be changed by the user. If it is set to a React element (only by the user),
|
||||
// that will not be persisted, and will be restored to default on load.
|
||||
title = "" as string | React.ReactElement;
|
||||
|
||||
// Number of threads that this script is running with
|
||||
threads = 1 as PositiveInteger;
|
||||
|
||||
@ -83,6 +93,7 @@ export class RunningScript {
|
||||
this.server = script.server;
|
||||
this.ramUsage = ramUsage;
|
||||
this.dependencies = script.dependencies;
|
||||
this.title = `${this.filename} ${args.join(" ")}`;
|
||||
}
|
||||
|
||||
log(txt: React.ReactNode): void {
|
||||
@ -140,7 +151,12 @@ export class RunningScript {
|
||||
|
||||
// Serialize the current object to a JSON save state
|
||||
toJSON(): IReviverValue {
|
||||
return Generic_toJSON("RunningScript", this, includedProperties);
|
||||
// Omit the title if it's a ReactNode, it will be filled in with the default on load.
|
||||
return Generic_toJSON(
|
||||
"RunningScript",
|
||||
this,
|
||||
typeof this.title === "string" ? includedProperties : includedPropsNoTitle,
|
||||
);
|
||||
}
|
||||
|
||||
// Initializes a RunningScript Object from a JSON save state
|
||||
@ -150,6 +166,9 @@ export class RunningScript {
|
||||
return runningScript;
|
||||
}
|
||||
}
|
||||
const includedProperties = getKeyList(RunningScript, { removedKeys: ["logs", "dependencies", "logUpd", "pid"] });
|
||||
const includedProperties = getKeyList(RunningScript, {
|
||||
removedKeys: ["logs", "dependencies", "logUpd", "pid", "tailProps"],
|
||||
});
|
||||
const includedPropsNoTitle = includedProperties.filter((x) => x !== "title");
|
||||
|
||||
constructorsForReviver.RunningScript = RunningScript;
|
||||
|
55
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
55
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -169,6 +169,29 @@ interface Multipliers {
|
||||
bladeburner_success_chance: number;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
interface TailProperties {
|
||||
/** X-coordinate of the log window */
|
||||
x: number;
|
||||
/** Y-coordinate of the log window */
|
||||
y: number;
|
||||
/** Width of the log window content area */
|
||||
width: number;
|
||||
/** Height of the log window content area */
|
||||
height: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A stand-in for the real React.ReactElement, which API-extractor doesn't know about.
|
||||
* Don't try to create one of these by hand; use React.createElement().
|
||||
*/
|
||||
interface ReactElement {
|
||||
type: string | ((props: any) => ReactElement | null) | (new (props: any) => object);
|
||||
props: any;
|
||||
key: string | number | null;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
interface RunningScript {
|
||||
/** Arguments the script was called with */
|
||||
@ -198,6 +221,15 @@ interface RunningScript {
|
||||
ramUsage: number;
|
||||
/** Hostname of the server on which this script runs */
|
||||
server: string;
|
||||
/** Properties of the tail window, or null if it is not shown */
|
||||
tailProperties: TailProperties | null;
|
||||
/**
|
||||
* The title, as shown in the script's log box. Defaults to the name + args,
|
||||
* but can be changed by the user. If it is set to a React element (only by
|
||||
* the user), that will not be persisted, and will be restored to default on
|
||||
* load.
|
||||
*/
|
||||
title: string | ReactElement;
|
||||
/** Number of threads that this script runs with */
|
||||
threads: number;
|
||||
/** Whether this RunningScript is excluded from saves */
|
||||
@ -5103,6 +5135,29 @@ export interface NS {
|
||||
*/
|
||||
closeTail(pid?: number): void;
|
||||
|
||||
/**
|
||||
* Set the title of the tail window of a script.
|
||||
* @remarks
|
||||
* RAM cost: 0 GB
|
||||
*
|
||||
* This sets the title to the given string, and also forces an update of the
|
||||
* tail window's contents.
|
||||
*
|
||||
* The title is saved across restarts, but only if it is a simple string.
|
||||
*
|
||||
* If the pid is unspecified, it will modify the current script’s logs.
|
||||
*
|
||||
* Otherwise, the pid argument can be used to change the logs from another script.
|
||||
*
|
||||
* It is possible to pass a React Element instead of a string. Get these by calling
|
||||
* React.createElement() with appropriate parameters. You should either know
|
||||
* or be willing to learn about the React UI library if you go down this
|
||||
* route, and there will likely be rough edges.
|
||||
*
|
||||
* @param pid - Optional. PID of the script having its tail closed. If omitted, the current script is used.
|
||||
*/
|
||||
setTitle(title: string | ReactElement, pid?: number): void;
|
||||
|
||||
/**
|
||||
* Get the list of servers connected to a server.
|
||||
* @remarks
|
||||
|
@ -4,13 +4,18 @@ import { RunningScript } from "../../Script/RunningScript";
|
||||
import { killWorkerScriptByPid } from "../../Netscript/killWorkerScript";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Box from "@mui/material/Box";
|
||||
import Button from "@mui/material/Button";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Draggable, { DraggableEvent } from "react-draggable";
|
||||
import { ResizableBox, ResizeCallbackData } from "react-resizable";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import makeStyles from "@mui/styles/makeStyles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import ArrowForwardIosIcon from "@mui/icons-material/ArrowForwardIos";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
|
||||
import StopCircleIcon from "@mui/icons-material/StopCircle";
|
||||
import PlayCircleIcon from "@mui/icons-material/PlayCircle";
|
||||
import { workerScripts } from "../../Netscript/WorkerScripts";
|
||||
import { startWorkerScript } from "../../NetscriptWorker";
|
||||
import { GetServer } from "../../Server/AllServers";
|
||||
@ -18,7 +23,6 @@ import { findRunningScriptByPid } from "../../Script/ScriptHelpers";
|
||||
import { debounce } from "lodash";
|
||||
import { Settings } from "../../Settings/Settings";
|
||||
import { ANSIITypography } from "./ANSIITypography";
|
||||
import { ScriptArg } from "../../Netscript/ScriptArg";
|
||||
import { useRerender } from "./hooks";
|
||||
import { dialogBoxCreate } from "./DialogBox";
|
||||
|
||||
@ -28,27 +32,47 @@ export const LogBoxEvents = new EventEmitter<[RunningScript]>();
|
||||
export const LogBoxCloserEvents = new EventEmitter<[number]>();
|
||||
export const LogBoxClearEvents = new EventEmitter<[]>();
|
||||
|
||||
interface LogBoxUIEvent<T> {
|
||||
pid: number;
|
||||
data: T;
|
||||
// Dynamic properties (size, position) bound to a specific rendered instance of a LogBox
|
||||
export class LogBoxProperties {
|
||||
x = window.innerWidth * 0.4;
|
||||
y = window.innerHeight * 0.3;
|
||||
width = 500;
|
||||
height = 500;
|
||||
|
||||
rerender: () => void;
|
||||
rootRef: React.RefObject<Draggable>;
|
||||
|
||||
constructor(rerender: () => void, rootRef: React.RefObject<Draggable>) {
|
||||
this.rerender = rerender;
|
||||
this.rootRef = rootRef;
|
||||
}
|
||||
|
||||
updateDOM(): void {
|
||||
if (!this.rootRef.current) return;
|
||||
const state = this.rootRef.current.state as { x: number; y: number };
|
||||
state.x = this.x;
|
||||
state.y = this.y;
|
||||
}
|
||||
|
||||
setPosition(x: number, y: number): void {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.updateDOM();
|
||||
}
|
||||
|
||||
setSize(width: number, height: number): void {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.rerender();
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
return this.rootRef.current !== null;
|
||||
}
|
||||
}
|
||||
|
||||
interface LogBoxPositionData {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export const LogBoxPositionEvents = new EventEmitter<[LogBoxUIEvent<LogBoxPositionData>]>();
|
||||
|
||||
interface LogBoxResizeData {
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
export const LogBoxSizeEvents = new EventEmitter<[LogBoxUIEvent<LogBoxResizeData>]>();
|
||||
|
||||
interface Log {
|
||||
id: string;
|
||||
id: number; // The PID of the script *when the window was first opened*
|
||||
script: RunningScript;
|
||||
}
|
||||
|
||||
@ -59,10 +83,9 @@ export function LogBoxManager(): React.ReactElement {
|
||||
useEffect(
|
||||
() =>
|
||||
LogBoxEvents.subscribe((script: RunningScript) => {
|
||||
const id = script.server + "-" + script.filename + script.args.map((x: ScriptArg): string => `${x}`).join("-");
|
||||
if (logs.find((l) => l.id === id)) return;
|
||||
if (logs.some((l) => l.script.pid === script.pid)) return;
|
||||
logs.push({
|
||||
id: id,
|
||||
id: script.pid,
|
||||
script: script,
|
||||
});
|
||||
rerender();
|
||||
@ -87,21 +110,21 @@ export function LogBoxManager(): React.ReactElement {
|
||||
);
|
||||
|
||||
//Close tail windows by their id
|
||||
function close(id: string): void {
|
||||
function close(id: number): void {
|
||||
logs = logs.filter((l) => l.id !== id);
|
||||
rerender();
|
||||
}
|
||||
|
||||
//Close tail windows by their pid
|
||||
//Close tail windows by their pid.
|
||||
function closePid(pid: number): void {
|
||||
logs = logs.filter((log) => log.script.pid != pid);
|
||||
logs = logs.filter((log) => log.script.pid !== pid);
|
||||
rerender();
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{logs.map((log) => (
|
||||
<LogWindow key={log.id} script={log.script} id={log.id} onClose={() => close(log.id)} />
|
||||
<LogWindow key={log.id} script={log.script} onClose={() => close(log.id)} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
@ -109,7 +132,6 @@ export function LogBoxManager(): React.ReactElement {
|
||||
|
||||
interface IProps {
|
||||
script: RunningScript;
|
||||
id: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
@ -124,7 +146,11 @@ const useStyles = makeStyles(() =>
|
||||
wordWrap: "break-word",
|
||||
},
|
||||
titleButton: {
|
||||
padding: "1px 0",
|
||||
borderWidth: "0 0 0 1px",
|
||||
borderColor: Settings.theme.welllight,
|
||||
borderStyle: "solid",
|
||||
borderRadius: "0",
|
||||
padding: "0",
|
||||
height: "100%",
|
||||
},
|
||||
}),
|
||||
@ -135,12 +161,13 @@ export const logBoxBaseZIndex = 1500;
|
||||
function LogWindow(props: IProps): React.ReactElement {
|
||||
const draggableRef = useRef<HTMLDivElement>(null);
|
||||
const rootRef = useRef<Draggable>(null);
|
||||
const [script, setScript] = useState(props.script);
|
||||
const script = props.script;
|
||||
const classes = useStyles();
|
||||
const container = useRef<HTMLDivElement>(null);
|
||||
const textArea = useRef<HTMLDivElement>(null);
|
||||
const rerender = useRerender(1000);
|
||||
const [size, setSize] = useState<[number, number]>([500, 500]);
|
||||
const propsRef = useRef(new LogBoxProperties(rerender, rootRef));
|
||||
script.tailProps = propsRef.current;
|
||||
const [minimized, setMinimized] = useState(false);
|
||||
|
||||
const textAreaKeyDown = (e: React.KeyboardEvent) => {
|
||||
@ -157,46 +184,17 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
};
|
||||
|
||||
const onResize = (e: React.SyntheticEvent, { size }: ResizeCallbackData) => {
|
||||
setSize([size.width, size.height]);
|
||||
propsRef.current.setSize(size.width, size.height);
|
||||
};
|
||||
|
||||
const setPosition = ({ x, y }: LogBoxPositionData) => {
|
||||
const node = rootRef.current;
|
||||
if (!node) return;
|
||||
const state = node.state as { x: number; y: number };
|
||||
state.x = x;
|
||||
state.y = y;
|
||||
};
|
||||
|
||||
// Listen to Logbox positioning events.
|
||||
useEffect(
|
||||
() =>
|
||||
LogBoxPositionEvents.subscribe((e) => {
|
||||
if (e.pid !== props.script.pid) return;
|
||||
setPosition(e.data);
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// Listen to Logbox resizing events.
|
||||
useEffect(
|
||||
() =>
|
||||
LogBoxSizeEvents.subscribe((e) => {
|
||||
if (e.pid !== props.script.pid) return;
|
||||
setSize([e.data.w, e.data.h]);
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
// initial position if 40%/30%
|
||||
useEffect(() => setPosition({ x: window.innerWidth * 0.4, y: window.innerHeight * 0.3 }), []);
|
||||
|
||||
useEffect(() => {
|
||||
propsRef.current.updateDOM();
|
||||
updateLayer();
|
||||
}, []);
|
||||
|
||||
function kill(): void {
|
||||
killWorkerScriptByPid(script.pid);
|
||||
rerender();
|
||||
}
|
||||
|
||||
function run(): void {
|
||||
@ -214,10 +212,17 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
if (!ramUsage) {
|
||||
return dialogBoxCreate(`Could not calculate ram usage for ${script.filename} on ${server.hostname}.`);
|
||||
}
|
||||
// Reset some things, because we're reusing the RunningScript instance
|
||||
script.ramUsage = ramUsage;
|
||||
script.dataMap = {};
|
||||
script.onlineExpGained = 0;
|
||||
script.onlineMoneyMade = 0;
|
||||
script.onlineRunningTime = 0.01;
|
||||
|
||||
startWorkerScript(script, server);
|
||||
rerender();
|
||||
} else {
|
||||
setScript(s);
|
||||
console.warn(`Tried to rerun pid ${script.pid} that was already running!`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,13 +234,17 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
rerender();
|
||||
}
|
||||
|
||||
function title(full = false): string {
|
||||
const maxLength = 30;
|
||||
const t = `${script.filename} ${script.args.join(" ")}`;
|
||||
if (full || t.length <= maxLength) {
|
||||
return t;
|
||||
}
|
||||
return t.slice(0, maxLength - 3) + "...";
|
||||
function title(): React.ReactElement {
|
||||
const title_str = script.title === "string" ? script.title : `${script.filename} ${script.args.join(" ")}`;
|
||||
return (
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ marginRight: "auto", textOverflow: "ellipsis", whiteSpace: "nowrap", overflow: "hidden" }}
|
||||
title={title_str}
|
||||
>
|
||||
{script.title}
|
||||
</Typography>
|
||||
);
|
||||
}
|
||||
|
||||
function minimize(): void {
|
||||
@ -271,7 +280,7 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
if (!node) return;
|
||||
|
||||
if (!isOnScreen(node)) {
|
||||
setPosition({ x: 0, y: 0 });
|
||||
propsRef.current.setPosition(0, 0);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
@ -290,7 +299,7 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
};
|
||||
|
||||
// Max [width, height]
|
||||
const minConstraints: [number, number] = [250, 33];
|
||||
const minConstraints: [number, number] = [150, 33];
|
||||
|
||||
return (
|
||||
<Draggable handle=".drag" onDrag={boundToBody} ref={rootRef} onMouseDown={updateLayer}>
|
||||
@ -316,8 +325,8 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
ref={container}
|
||||
>
|
||||
<ResizableBox
|
||||
width={size[0]}
|
||||
height={size[1]}
|
||||
width={propsRef.current.width}
|
||||
height={propsRef.current.height}
|
||||
onResize={onResize}
|
||||
minConstraints={minConstraints}
|
||||
handle={
|
||||
@ -336,30 +345,24 @@ function LogWindow(props: IProps): React.ReactElement {
|
||||
>
|
||||
<>
|
||||
<Paper className="drag" sx={{ display: "flex", alignItems: "center", cursor: "grab" }} ref={draggableRef}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
sx={{ marginRight: "auto", textOverflow: "ellipsis", whiteSpace: "nowrap", overflow: "hidden" }}
|
||||
title={title(true)}
|
||||
>
|
||||
{title(true)}
|
||||
</Typography>
|
||||
{title()}
|
||||
|
||||
<span style={{ minWidth: "fit-content", height: `${minConstraints[1]}px` }}>
|
||||
{!workerScripts.has(script.pid) ? (
|
||||
<Button className={classes.titleButton} onClick={run} onTouchEnd={run}>
|
||||
Run
|
||||
</Button>
|
||||
<IconButton className={classes.titleButton} onClick={run} onTouchEnd={run}>
|
||||
<PlayCircleIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button className={classes.titleButton} onClick={kill} onTouchEnd={kill}>
|
||||
Kill
|
||||
</Button>
|
||||
<IconButton className={classes.titleButton} onClick={kill} onTouchEnd={kill}>
|
||||
<StopCircleIcon color="error" />
|
||||
</IconButton>
|
||||
)}
|
||||
<Button className={classes.titleButton} onClick={minimize} onTouchEnd={minimize}>
|
||||
{minimized ? "\u{1F5D6}" : "\u{1F5D5}"}
|
||||
</Button>
|
||||
<Button className={classes.titleButton} onClick={props.onClose} onTouchEnd={props.onClose}>
|
||||
Close
|
||||
</Button>
|
||||
<IconButton className={classes.titleButton} onClick={minimize} onTouchEnd={minimize}>
|
||||
{minimized ? <ExpandMoreIcon /> : <ExpandLessIcon />}
|
||||
</IconButton>
|
||||
<IconButton className={classes.titleButton} onClick={props.onClose} onTouchEnd={props.onClose}>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</span>
|
||||
</Paper>
|
||||
|
||||
|
@ -77,6 +77,7 @@ function loadStandardServers() {
|
||||
"ramUsage": 1.6,
|
||||
"server": "home",
|
||||
"scriptKey": "script.js*[]",
|
||||
"title": "Awesome Script",
|
||||
"dependencies": [
|
||||
{
|
||||
"filename": "script.js",
|
||||
|
@ -74,6 +74,7 @@ exports[`load/saveAllServers 1`] = `
|
||||
"ramUsage": 1.6,
|
||||
"server": "home",
|
||||
"scriptKey": "script.js*[]",
|
||||
"title": "Awesome Script",
|
||||
"threads": 1,
|
||||
"temporary": false
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user