mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-07-20 19:44:46 +01:00
TGUI 5.0 Patch 2 ✨ (#15737)
This commit is contained in:
Vendored
+4
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"eslint.nodePath": "./tgui/.yarn/sdks",
|
||||
"eslint.workingDirectories": ["./tgui"],
|
||||
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.js",
|
||||
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs",
|
||||
"typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"search.exclude": {
|
||||
@@ -11,6 +11,9 @@
|
||||
"workbench.editorAssociations": {
|
||||
"*.dmi": "imagePreview.previewEditor"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"files.eol": "\n",
|
||||
"files.encoding": "utf8",
|
||||
"files.insertFinalNewline": true,
|
||||
|
||||
@@ -1,15 +1 @@
|
||||
arrowParens: always
|
||||
breakLongMethodChains: true
|
||||
endOfLine: lf
|
||||
importFormatting: oneline
|
||||
jsxBracketSameLine: true
|
||||
jsxSingleQuote: false
|
||||
offsetTernaryExpressions: true
|
||||
printWidth: 80
|
||||
proseWrap: preserve
|
||||
quoteProps: preserve
|
||||
semi: true
|
||||
singleQuote: true
|
||||
tabWidth: 2
|
||||
trailingComma: es5
|
||||
useTabs: false
|
||||
|
||||
Vendored
+4
-1
@@ -2,5 +2,8 @@
|
||||
"name": "eslint",
|
||||
"version": "7.32.0-sdk",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs"
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"eslint": "./bin/eslint.js"
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
|
||||
const relPnpApiPath = '../../../.pnp.cjs';
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real prettier your application uses
|
||||
module.exports = absRequire(`prettier`);
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/index.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real prettier/index.js your application uses
|
||||
module.exports = absRequire(`prettier/index.js`);
|
||||
+4
-3
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"name": "prettier",
|
||||
"version": "0.19.0-sdk",
|
||||
"main": "./index.js",
|
||||
"type": "commonjs"
|
||||
"version": "3.1.0-sdk",
|
||||
"main": "./index.cjs",
|
||||
"type": "commonjs",
|
||||
"bin": "./bin/prettier.cjs"
|
||||
}
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/bin/prettier.cjs
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real prettier/bin/prettier.cjs your application uses
|
||||
module.exports = absRequire(`prettier/bin/prettier.cjs`);
|
||||
+118
-69
@@ -1,29 +1,31 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = tsserver => {
|
||||
const moduleWrapper = (tsserver) => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const {isAbsolute} = require(`path`);
|
||||
const { isAbsolute } = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = str => str.startsWith("portal:/");
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = (str) => str.startsWith('portal:/');
|
||||
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
const dependencyTreeRoots = new Set(
|
||||
pnpApi.getDependencyTreeRoots().map((locator) => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
})
|
||||
);
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
@@ -31,7 +33,11 @@ const moduleWrapper = tsserver => {
|
||||
|
||||
function toEditorPath(str) {
|
||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||
if (
|
||||
isAbsolute(str) &&
|
||||
!str.match(/^\^?(zip:|\/zip\/)/) &&
|
||||
(str.match(/\.zip\//) || isVirtual(str))
|
||||
) {
|
||||
// We also take the opportunity to turn virtual paths into physical ones;
|
||||
// this makes it much easier to work with workspaces that list peer
|
||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||
@@ -45,7 +51,11 @@ const moduleWrapper = tsserver => {
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
if (
|
||||
locator &&
|
||||
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) ||
|
||||
isPortal(locator.reference))
|
||||
) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
@@ -73,42 +83,58 @@ const moduleWrapper = tsserver => {
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
case `vscode <1.61`:
|
||||
{
|
||||
str = `^zip:${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
case `vscode <1.66`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
case `vscode <1.68`:
|
||||
{
|
||||
str = `^/zip${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
case `vscode`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
}
|
||||
break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
case `neovim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,26 +143,35 @@ const moduleWrapper = tsserver => {
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
}
|
||||
break;
|
||||
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
case `neovim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode`:
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
return str.replace(
|
||||
/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/,
|
||||
process.platform === `win32` ? `` : `/`
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +183,9 @@ const moduleWrapper = tsserver => {
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } =
|
||||
ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
@@ -159,7 +195,8 @@ const moduleWrapper = tsserver => {
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
const { onMessage: originalOnMessage, send: originalSend } =
|
||||
Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
@@ -175,10 +212,12 @@ const moduleWrapper = tsserver => {
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
const [, major, minor] = (
|
||||
process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []
|
||||
).map(Number);
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
@@ -192,21 +231,31 @@ const moduleWrapper = tsserver => {
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
});
|
||||
const processedMessageJSON = JSON.stringify(
|
||||
parsedMessage,
|
||||
(key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
}
|
||||
);
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||
isStringMessage
|
||||
? processedMessageJSON
|
||||
: JSON.parse(processedMessageJSON)
|
||||
);
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
return originalSend.call(
|
||||
this,
|
||||
JSON.parse(
|
||||
JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
|
||||
+118
-69
@@ -1,29 +1,31 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = tsserver => {
|
||||
const moduleWrapper = (tsserver) => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const {isAbsolute} = require(`path`);
|
||||
const { isAbsolute } = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = str => str.startsWith("portal:/");
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
const isVirtual = (str) => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = (str) => str.startsWith('portal:/');
|
||||
const normalize = (str) => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
const dependencyTreeRoots = new Set(
|
||||
pnpApi.getDependencyTreeRoots().map((locator) => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
})
|
||||
);
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
@@ -31,7 +33,11 @@ const moduleWrapper = tsserver => {
|
||||
|
||||
function toEditorPath(str) {
|
||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||
if (
|
||||
isAbsolute(str) &&
|
||||
!str.match(/^\^?(zip:|\/zip\/)/) &&
|
||||
(str.match(/\.zip\//) || isVirtual(str))
|
||||
) {
|
||||
// We also take the opportunity to turn virtual paths into physical ones;
|
||||
// this makes it much easier to work with workspaces that list peer
|
||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||
@@ -45,7 +51,11 @@ const moduleWrapper = tsserver => {
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
if (
|
||||
locator &&
|
||||
(dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) ||
|
||||
isPortal(locator.reference))
|
||||
) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
@@ -73,42 +83,58 @@ const moduleWrapper = tsserver => {
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
case `vscode <1.61`:
|
||||
{
|
||||
str = `^zip:${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
case `vscode <1.66`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
case `vscode <1.68`:
|
||||
{
|
||||
str = `^/zip${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
case `vscode`:
|
||||
{
|
||||
str = `^/zip/${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
}
|
||||
break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
case `neovim`:
|
||||
{
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
}
|
||||
break;
|
||||
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
str = `zip:${str}`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,26 +143,35 @@ const moduleWrapper = tsserver => {
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
case `coc-nvim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
}
|
||||
break;
|
||||
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
case `neovim`:
|
||||
{
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
}
|
||||
break;
|
||||
|
||||
case `vscode`:
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
return str.replace(
|
||||
/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/,
|
||||
process.platform === `win32` ? `` : `/`
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +183,9 @@ const moduleWrapper = tsserver => {
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
const { enablePluginsWithOptions: originalEnablePluginsWithOptions } =
|
||||
ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function () {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
@@ -159,7 +195,8 @@ const moduleWrapper = tsserver => {
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
const { onMessage: originalOnMessage, send: originalSend } =
|
||||
Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
@@ -175,10 +212,12 @@ const moduleWrapper = tsserver => {
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
const [, major, minor] = (
|
||||
process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []
|
||||
).map(Number);
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
@@ -192,21 +231,31 @@ const moduleWrapper = tsserver => {
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
});
|
||||
const processedMessageJSON = JSON.stringify(
|
||||
parsedMessage,
|
||||
(key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
}
|
||||
);
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||
isStringMessage
|
||||
? processedMessageJSON
|
||||
: JSON.parse(processedMessageJSON)
|
||||
);
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
return originalSend.call(
|
||||
this,
|
||||
JSON.parse(
|
||||
JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
|
||||
+7
-7
@@ -1,20 +1,20 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, createRequireFromPath} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const { existsSync } = require(`fs`);
|
||||
const { createRequire, createRequireFromPath } = require(`module`);
|
||||
const { resolve } = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
const relPnpApiPath = '../../../../.pnp.cjs';
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/typescript.js
|
||||
// Setup the environment to be able to require typescript
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/typescript.js your application uses
|
||||
module.exports = absRequire(`typescript/lib/typescript.js`);
|
||||
// Defer to the real typescript your application uses
|
||||
module.exports = absRequire(`typescript`);
|
||||
|
||||
+5
-1
@@ -2,5 +2,9 @@
|
||||
"name": "typescript",
|
||||
"version": "4.3.5-sdk",
|
||||
"main": "./lib/typescript.js",
|
||||
"type": "commonjs"
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"tsc": "./bin/tsc",
|
||||
"tsserver": "./bin/tsserver"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ function task-prettier {
|
||||
}
|
||||
|
||||
function task-prettify {
|
||||
yarn prettierx --write packages @Args
|
||||
yarn prettier --write packages @Args
|
||||
}
|
||||
|
||||
## Run a linter through all packages
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
"tgui:build": "BROWSERSLIST_IGNORE_OLD_DATA=true webpack",
|
||||
"tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js",
|
||||
"tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx",
|
||||
"tgui:prettier": "prettierx --check .",
|
||||
"tgui:prettier": "prettier --check .",
|
||||
"tgui:sonar": "eslint packages -c .eslintrc-sonar.yml",
|
||||
"tgui:test": "jest --watch",
|
||||
"tgui:test-simple": "CI=true jest --color",
|
||||
@@ -42,7 +42,7 @@
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"mini-css-extract-plugin": "^2.7.6",
|
||||
"prettier": "npm:prettierx@0.19.0",
|
||||
"prettier": "^3.1.0",
|
||||
"sass": "^1.69.5",
|
||||
"sass-loader": "^13.3.2",
|
||||
"style-loader": "^3.3.3",
|
||||
|
||||
@@ -32,12 +32,12 @@ export const filter =
|
||||
};
|
||||
|
||||
type MapFunction = {
|
||||
<T, U>(iterateeFn: (value: T, index: number, collection: T[]) => U): (
|
||||
collection: T[]
|
||||
) => U[];
|
||||
<T, U>(
|
||||
iterateeFn: (value: T, index: number, collection: T[]) => U,
|
||||
): (collection: T[]) => U[];
|
||||
|
||||
<T, U, K extends string | number>(
|
||||
iterateeFn: (value: T, index: K, collection: Record<K, T>) => U
|
||||
iterateeFn: (value: T, index: K, collection: Record<K, T>) => U,
|
||||
): (collection: Record<K, T>) => U[];
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ export const map: MapFunction =
|
||||
*/
|
||||
export const filterMap = <T, U>(
|
||||
collection: T[],
|
||||
iterateeFn: (value: T) => U | undefined
|
||||
iterateeFn: (value: T) => U | undefined,
|
||||
): U[] => {
|
||||
const finalCollection: U[] = [];
|
||||
|
||||
@@ -261,7 +261,7 @@ export const zipWith =
|
||||
const binarySearch = <T, U = unknown>(
|
||||
getKey: (value: T) => U,
|
||||
collection: readonly T[],
|
||||
inserting: T
|
||||
inserting: T,
|
||||
): number => {
|
||||
if (collection.length === 0) {
|
||||
return 0;
|
||||
|
||||
@@ -30,7 +30,7 @@ export class Color {
|
||||
this.r - this.r * percent,
|
||||
this.g - this.g * percent,
|
||||
this.b - this.b * percent,
|
||||
this.a
|
||||
this.a,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ Color.fromHex = (hex) =>
|
||||
new Color(
|
||||
parseInt(hex.substr(1, 2), 16),
|
||||
parseInt(hex.substr(3, 2), 16),
|
||||
parseInt(hex.substr(5, 2), 16)
|
||||
parseInt(hex.substr(5, 2), 16),
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -59,7 +59,7 @@ Color.lerp = (c1, c2, n) =>
|
||||
(c2.r - c1.r) * n + c1.r,
|
||||
(c2.g - c1.g) * n + c1.g,
|
||||
(c2.b - c1.b) * n + c1.b,
|
||||
(c2.a - c1.a) * n + c1.a
|
||||
(c2.a - c1.a) * n + c1.a,
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { Action, applyMiddleware, combineReducers, createAction, createStore, Reducer } from './redux';
|
||||
import {
|
||||
Action,
|
||||
applyMiddleware,
|
||||
combineReducers,
|
||||
createAction,
|
||||
createStore,
|
||||
Reducer,
|
||||
} from './redux';
|
||||
|
||||
// Dummy Reducer
|
||||
const counterReducer: Reducer<number, Action<string>> = (state = 0, action) => {
|
||||
@@ -31,7 +38,7 @@ describe('Redux implementation tests', () => {
|
||||
test('createStore with applyMiddleware works', () => {
|
||||
const store = createStore(
|
||||
counterReducer,
|
||||
applyMiddleware(loggingMiddleware)
|
||||
applyMiddleware(loggingMiddleware),
|
||||
);
|
||||
expect(store.getState()).toBe(0);
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
export type Reducer<State = any, ActionType extends Action = AnyAction> = (
|
||||
state: State | undefined,
|
||||
action: ActionType
|
||||
action: ActionType,
|
||||
) => State;
|
||||
|
||||
export type Store<State = any, ActionType extends Action = AnyAction> = {
|
||||
@@ -21,7 +21,7 @@ type MiddlewareAPI<State = any, ActionType extends Action = AnyAction> = {
|
||||
};
|
||||
|
||||
export type Middleware = <State = any, ActionType extends Action = AnyAction>(
|
||||
storeApi: MiddlewareAPI<State, ActionType>
|
||||
storeApi: MiddlewareAPI<State, ActionType>,
|
||||
) => (next: Dispatch<ActionType>) => Dispatch<ActionType>;
|
||||
|
||||
export type Action<TType = any> = {
|
||||
@@ -33,7 +33,7 @@ export type AnyAction = Action & {
|
||||
};
|
||||
|
||||
export type Dispatch<ActionType extends Action = AnyAction> = (
|
||||
action: ActionType
|
||||
action: ActionType,
|
||||
) => void;
|
||||
|
||||
type StoreEnhancer = (createStoreFunction: Function) => Function;
|
||||
@@ -48,7 +48,7 @@ type PreparedAction = {
|
||||
*/
|
||||
export const createStore = <State, ActionType extends Action = AnyAction>(
|
||||
reducer: Reducer<State, ActionType>,
|
||||
enhancer?: StoreEnhancer
|
||||
enhancer?: StoreEnhancer,
|
||||
): Store<State, ActionType> => {
|
||||
// Apply a store enhancer (applyMiddleware is one of them).
|
||||
if (enhancer) {
|
||||
@@ -90,14 +90,14 @@ export const applyMiddleware = (
|
||||
...middlewares: Middleware[]
|
||||
): StoreEnhancer => {
|
||||
return (
|
||||
createStoreFunction: (reducer: Reducer, enhancer?: StoreEnhancer) => Store
|
||||
createStoreFunction: (reducer: Reducer, enhancer?: StoreEnhancer) => Store,
|
||||
) => {
|
||||
return (reducer, ...args): Store => {
|
||||
const store = createStoreFunction(reducer, ...args);
|
||||
|
||||
let dispatch: Dispatch = () => {
|
||||
throw new Error(
|
||||
'Dispatching while constructing your middleware is not allowed.'
|
||||
'Dispatching while constructing your middleware is not allowed.',
|
||||
);
|
||||
};
|
||||
|
||||
@@ -109,7 +109,7 @@ export const applyMiddleware = (
|
||||
const chain = middlewares.map((middleware) => middleware(storeApi));
|
||||
dispatch = chain.reduceRight(
|
||||
(next, middleware) => middleware(next),
|
||||
store.dispatch
|
||||
store.dispatch,
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -129,7 +129,7 @@ export const applyMiddleware = (
|
||||
* is also more flexible than the redux counterpart.
|
||||
*/
|
||||
export const combineReducers = (
|
||||
reducersObj: Record<string, Reducer>
|
||||
reducersObj: Record<string, Reducer>,
|
||||
): Reducer => {
|
||||
const keys = Object.keys(reducersObj);
|
||||
|
||||
@@ -170,7 +170,7 @@ export const combineReducers = (
|
||||
*/
|
||||
export const createAction = <TAction extends string>(
|
||||
type: TAction,
|
||||
prepare?: (...args: any[]) => PreparedAction
|
||||
prepare?: (...args: any[]) => PreparedAction,
|
||||
) => {
|
||||
const actionCreator = (...args: any[]) => {
|
||||
let action: Action<TAction> & PreparedAction = { type };
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
export const debounce = <F extends (...args: any[]) => any>(
|
||||
fn: F,
|
||||
time: number,
|
||||
immediate = false
|
||||
immediate = false,
|
||||
): ((...args: Parameters<F>) => void) => {
|
||||
let timeout: ReturnType<typeof setTimeout> | null;
|
||||
return (...args: Parameters<F>) => {
|
||||
@@ -38,7 +38,7 @@ export const debounce = <F extends (...args: any[]) => any>(
|
||||
*/
|
||||
export const throttle = <F extends (...args: any[]) => any>(
|
||||
fn: F,
|
||||
time: number
|
||||
time: number,
|
||||
): ((...args: Parameters<F>) => void) => {
|
||||
let previouslyRun: number | null,
|
||||
queuedToRun: ReturnType<typeof setTimeout> | null;
|
||||
@@ -53,7 +53,7 @@ export const throttle = <F extends (...args: any[]) => any>(
|
||||
} else {
|
||||
queuedToRun = setTimeout(
|
||||
() => invokeFn(...args),
|
||||
time - (now - (previouslyRun ?? 0))
|
||||
time - (now - (previouslyRun ?? 0)),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Vendored
+2
-1
@@ -1,6 +1,7 @@
|
||||
@font-face {
|
||||
font-family: 'tgfont';
|
||||
src: url('./tgfont.woff2?958b912b123580c55529f68c4e2261bd') format('woff2'),
|
||||
src:
|
||||
url('./tgfont.woff2?958b912b123580c55529f68c4e2261bd') format('woff2'),
|
||||
url('./tgfont.eot?958b912b123580c55529f68c4e2261bd#iefix')
|
||||
format('embedded-opentype');
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ declare class Benchmark {
|
||||
static reduce<T, K>(
|
||||
arr: T[],
|
||||
callback: (accumulator: K, value: T) => K,
|
||||
thisArg?: any
|
||||
thisArg?: any,
|
||||
): K;
|
||||
|
||||
static options: Benchmark.Options;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ const renderUi = createRenderer((dataJson: string) => {
|
||||
store.dispatch(
|
||||
backendUpdate({
|
||||
data: Byond.parseJson(dataJson),
|
||||
})
|
||||
}),
|
||||
);
|
||||
return <DisposalBin />;
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ export const ListOfTooltips = () => {
|
||||
<Box as="span" backgroundColor="blue" fontSize="48px" m={1}>
|
||||
Tooltip #{i}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
</Tooltip>,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export class DreamSeeker {
|
||||
+ '=' + encodeURIComponent(params[key]))
|
||||
.join('&');
|
||||
logger.log(
|
||||
`topic call at ${this.client.defaults.baseURL + '/dummy?' + query}`
|
||||
`topic call at ${this.client.defaults.baseURL + '/dummy?' + query}`,
|
||||
);
|
||||
return this.client.get('/dummy?' + query);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ export const loadSourceMaps = async (bundleDir) => {
|
||||
try {
|
||||
const file = basename(path).replace('.map', '');
|
||||
const consumer = await new SourceMapConsumer(
|
||||
JSON.parse(fs.readFileSync(path, 'utf8'))
|
||||
JSON.parse(fs.readFileSync(path, 'utf8')),
|
||||
);
|
||||
sourceMaps.push({ file, consumer });
|
||||
} catch (err) {
|
||||
|
||||
@@ -84,19 +84,19 @@ export const reloadByondCache = async (bundleDir) => {
|
||||
}
|
||||
// Get dreamseeker instances
|
||||
const pids = cacheDirs.map((cacheDir) =>
|
||||
parseInt(cacheDir.split('/cache/tmp').pop(), 10)
|
||||
parseInt(cacheDir.split('/cache/tmp').pop(), 10),
|
||||
);
|
||||
const dssPromise = DreamSeeker.getInstancesByPids(pids);
|
||||
// Copy assets
|
||||
const assets = await resolveGlob(
|
||||
bundleDir,
|
||||
'./*.+(bundle|chunk|hot-update).*'
|
||||
'./*.+(bundle|chunk|hot-update).*',
|
||||
);
|
||||
for (let cacheDir of cacheDirs) {
|
||||
// Clear garbage
|
||||
const garbage = await resolveGlob(
|
||||
cacheDir,
|
||||
'./*.+(bundle|chunk|hot-update).*'
|
||||
'./*.+(bundle|chunk|hot-update).*',
|
||||
);
|
||||
try {
|
||||
// Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 515.
|
||||
|
||||
@@ -22,10 +22,10 @@ export const NowPlayingWidget = (props) => {
|
||||
duration = audio.meta?.duration,
|
||||
date = !isNaN(upload_date)
|
||||
? upload_date?.substring(0, 4) +
|
||||
'-' +
|
||||
upload_date?.substring(4, 6) +
|
||||
'-' +
|
||||
upload_date?.substring(6, 8)
|
||||
'-' +
|
||||
upload_date?.substring(4, 6) +
|
||||
'-' +
|
||||
upload_date?.substring(6, 8)
|
||||
: upload_date;
|
||||
|
||||
return (
|
||||
@@ -38,7 +38,8 @@ export const NowPlayingWidget = (props) => {
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{
|
||||
<Collapsible title={title || 'Unknown Track'} color={'blue'}>
|
||||
<Section>
|
||||
|
||||
@@ -5,8 +5,21 @@
|
||||
*/
|
||||
|
||||
import { useDispatch, useSelector } from 'tgui/backend';
|
||||
import { Button, Collapsible, Divider, Input, Section, Stack } from 'tgui/components';
|
||||
import { moveChatPageLeft, moveChatPageRight, removeChatPage, toggleAcceptedType, updateChatPage } from './actions';
|
||||
import {
|
||||
Button,
|
||||
Collapsible,
|
||||
Divider,
|
||||
Input,
|
||||
Section,
|
||||
Stack,
|
||||
} from 'tgui/components';
|
||||
import {
|
||||
moveChatPageLeft,
|
||||
moveChatPageRight,
|
||||
removeChatPage,
|
||||
toggleAcceptedType,
|
||||
updateChatPage,
|
||||
} from './actions';
|
||||
import { MESSAGE_TYPES } from './constants';
|
||||
import { selectCurrentChatPage } from './selectors';
|
||||
|
||||
@@ -25,7 +38,7 @@ export const ChatPageSettings = (props) => {
|
||||
updateChatPage({
|
||||
pageId: page.id,
|
||||
name: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -39,9 +52,10 @@ export const ChatPageSettings = (props) => {
|
||||
dispatch(
|
||||
removeChatPage({
|
||||
pageId: page.id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
@@ -60,9 +74,10 @@ export const ChatPageSettings = (props) => {
|
||||
dispatch(
|
||||
moveChatPageLeft({
|
||||
pageId: page.id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
«
|
||||
</Button>
|
||||
<Button
|
||||
@@ -71,9 +86,10 @@ export const ChatPageSettings = (props) => {
|
||||
dispatch(
|
||||
moveChatPageRight({
|
||||
pageId: page.id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
»
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
@@ -84,7 +100,7 @@ export const ChatPageSettings = (props) => {
|
||||
<Divider />
|
||||
<Section title="Messages to display" level={2}>
|
||||
{MESSAGE_TYPES.filter(
|
||||
(typeDef) => !typeDef.important && !typeDef.admin
|
||||
(typeDef) => !typeDef.important && !typeDef.admin,
|
||||
).map((typeDef) => (
|
||||
<Button.Checkbox
|
||||
key={typeDef.type}
|
||||
@@ -94,15 +110,16 @@ export const ChatPageSettings = (props) => {
|
||||
toggleAcceptedType({
|
||||
pageId: page.id,
|
||||
type: typeDef.type,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
{typeDef.name}
|
||||
</Button.Checkbox>
|
||||
))}
|
||||
<Collapsible mt={1} color="transparent" title="Admin stuff">
|
||||
{MESSAGE_TYPES.filter(
|
||||
(typeDef) => !typeDef.important && typeDef.admin
|
||||
(typeDef) => !typeDef.important && typeDef.admin,
|
||||
).map((typeDef) => (
|
||||
<Button.Checkbox
|
||||
key={typeDef.type}
|
||||
@@ -112,9 +129,10 @@ export const ChatPageSettings = (props) => {
|
||||
toggleAcceptedType({
|
||||
pageId: page.id,
|
||||
type: typeDef.type,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
{typeDef.name}
|
||||
</Button.Checkbox>
|
||||
))}
|
||||
|
||||
@@ -26,7 +26,7 @@ export class ChatPanel extends Component {
|
||||
chatRenderer.mount(this.ref.current);
|
||||
chatRenderer.events.on(
|
||||
'scrollTrackingChanged',
|
||||
this.handleScrollTrackingChange
|
||||
this.handleScrollTrackingChange,
|
||||
);
|
||||
this.componentDidUpdate();
|
||||
}
|
||||
@@ -34,7 +34,7 @@ export class ChatPanel extends Component {
|
||||
componentWillUnmount() {
|
||||
chatRenderer.events.off(
|
||||
'scrollTrackingChanged',
|
||||
this.handleScrollTrackingChange
|
||||
this.handleScrollTrackingChange,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,8 @@ export class ChatPanel extends Component {
|
||||
<Button
|
||||
className="Chat__scrollButton"
|
||||
icon="arrow-down"
|
||||
onClick={() => chatRenderer.scrollToBottom()}>
|
||||
onClick={() => chatRenderer.scrollToBottom()}
|
||||
>
|
||||
Scroll to bottom
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -19,7 +19,8 @@ const UnreadCountWidget = ({ value }) => (
|
||||
lineHeight: '1.55em',
|
||||
backgroundColor: 'crimson',
|
||||
color: '#fff',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{Math.min(value, 99)}
|
||||
</Box>
|
||||
);
|
||||
@@ -45,9 +46,10 @@ export const ChatTabs = (props) => {
|
||||
dispatch(
|
||||
changeChatPage({
|
||||
pageId: page.id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
{page.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
|
||||
@@ -7,9 +7,28 @@
|
||||
import { storage } from 'common/storage';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
import { addHighlightSetting, loadSettings, removeHighlightSetting, updateHighlightSetting, updateSettings } from '../settings/actions';
|
||||
import {
|
||||
addHighlightSetting,
|
||||
loadSettings,
|
||||
removeHighlightSetting,
|
||||
updateHighlightSetting,
|
||||
updateSettings,
|
||||
} from '../settings/actions';
|
||||
import { selectSettings } from '../settings/selectors';
|
||||
import { addChatPage, changeChatPage, changeScrollTracking, loadChat, moveChatPageLeft, moveChatPageRight, purgeChatMessageArchive, rebuildChat, removeChatPage, saveChatToDisk, toggleAcceptedType, updateMessageCount } from './actions';
|
||||
import {
|
||||
addChatPage,
|
||||
changeChatPage,
|
||||
changeScrollTracking,
|
||||
loadChat,
|
||||
moveChatPageLeft,
|
||||
moveChatPageRight,
|
||||
purgeChatMessageArchive,
|
||||
rebuildChat,
|
||||
removeChatPage,
|
||||
saveChatToDisk,
|
||||
toggleAcceptedType,
|
||||
updateMessageCount,
|
||||
} from './actions';
|
||||
import { MESSAGE_SAVE_INTERVAL } from './constants';
|
||||
import { createMessage, serializeMessage } from './model';
|
||||
import { chatRenderer } from './renderer';
|
||||
@@ -23,7 +42,7 @@ const saveChatToStorage = async (store) => {
|
||||
const settings = selectSettings(store.getState());
|
||||
const fromIndex = Math.max(
|
||||
0,
|
||||
chatRenderer.messages.length - settings.persistentMessageLimit
|
||||
chatRenderer.messages.length - settings.persistentMessageLimit,
|
||||
);
|
||||
const messages = chatRenderer.messages
|
||||
.slice(fromIndex)
|
||||
@@ -32,7 +51,7 @@ const saveChatToStorage = async (store) => {
|
||||
storage.set('chat-messages', messages);
|
||||
storage.set(
|
||||
'chat-messages-archive',
|
||||
chatRenderer.archivedMessages.map((message) => serializeMessage(message))
|
||||
chatRenderer.archivedMessages.map((message) => serializeMessage(message)),
|
||||
); // FIXME: Better chat history
|
||||
};
|
||||
|
||||
@@ -123,7 +142,7 @@ export const chatMiddleware = (store) => {
|
||||
settings.visibleMessageLimit,
|
||||
settings.combineMessageLimit,
|
||||
settings.combineIntervalLimit,
|
||||
settings.logLineCount
|
||||
settings.logLineCount,
|
||||
);
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
@@ -205,7 +224,7 @@ export const chatMiddleware = (store) => {
|
||||
next(action);
|
||||
chatRenderer.setHighlight(
|
||||
settings.highlightSettings,
|
||||
settings.highlightSettingById
|
||||
settings.highlightSettingById,
|
||||
);
|
||||
|
||||
return;
|
||||
|
||||
@@ -4,7 +4,18 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { addChatPage, changeChatPage, changeScrollTracking, loadChat, moveChatPageLeft, moveChatPageRight, removeChatPage, toggleAcceptedType, updateChatPage, updateMessageCount } from './actions';
|
||||
import {
|
||||
addChatPage,
|
||||
changeChatPage,
|
||||
changeScrollTracking,
|
||||
loadChat,
|
||||
moveChatPageLeft,
|
||||
moveChatPageRight,
|
||||
removeChatPage,
|
||||
toggleAcceptedType,
|
||||
updateChatPage,
|
||||
updateMessageCount,
|
||||
} from './actions';
|
||||
import { canPageAcceptType, createMainPage } from './model';
|
||||
|
||||
const mainPage = createMainPage();
|
||||
|
||||
@@ -7,9 +7,22 @@
|
||||
import { EventEmitter } from 'common/events';
|
||||
import { classes } from 'common/react';
|
||||
import { createLogger } from 'tgui/logging';
|
||||
import { IMAGE_RETRY_DELAY, IMAGE_RETRY_LIMIT, IMAGE_RETRY_MESSAGE_AGE, MESSAGE_PRUNE_INTERVAL, MESSAGE_TYPES, MESSAGE_TYPE_INTERNAL, MESSAGE_TYPE_UNKNOWN } from './constants';
|
||||
import {
|
||||
IMAGE_RETRY_DELAY,
|
||||
IMAGE_RETRY_LIMIT,
|
||||
IMAGE_RETRY_MESSAGE_AGE,
|
||||
MESSAGE_PRUNE_INTERVAL,
|
||||
MESSAGE_TYPES,
|
||||
MESSAGE_TYPE_INTERNAL,
|
||||
MESSAGE_TYPE_UNKNOWN,
|
||||
} from './constants';
|
||||
import { render } from 'react-dom';
|
||||
import { canPageAcceptType, createMessage, isSameMessage, serializeMessage } from './model';
|
||||
import {
|
||||
canPageAcceptType,
|
||||
createMessage,
|
||||
isSameMessage,
|
||||
serializeMessage,
|
||||
} from './model';
|
||||
import { highlightNode, linkifyNode } from './replaceInTextNode';
|
||||
import { Tooltip } from '../../tgui/components';
|
||||
|
||||
@@ -27,8 +40,8 @@ export const TGUI_CHAT_COMPONENTS = {
|
||||
// List of injectable attibute names mapped to their proper prop
|
||||
// We need this because attibutes don't support lowercase names
|
||||
export const TGUI_CHAT_ATTRIBUTES_TO_PROPS = {
|
||||
'position': 'position',
|
||||
'content': 'content',
|
||||
position: 'position',
|
||||
content: 'content',
|
||||
};
|
||||
|
||||
const findNearestScrollableParent = (startingNode) => {
|
||||
@@ -213,7 +226,7 @@ class ChatRenderer {
|
||||
// Must be alphanumeric (with some punctuation)
|
||||
allowedRegex.test(str) &&
|
||||
// Reset lastIndex so it does not mess up the next word
|
||||
((allowedRegex.lastIndex = 0) || true)
|
||||
((allowedRegex.lastIndex = 0) || true),
|
||||
);
|
||||
let highlightWords;
|
||||
let highlightRegex;
|
||||
@@ -232,7 +245,7 @@ class ChatRenderer {
|
||||
// Must be alphanumeric (with some punctuation)
|
||||
allowedRegex.test(str) &&
|
||||
// Reset lastIndex so it does not mess up the next word
|
||||
((allowedRegex.lastIndex = 0) || true)
|
||||
((allowedRegex.lastIndex = 0) || true),
|
||||
);
|
||||
let blacklistWords;
|
||||
let blacklistregex;
|
||||
@@ -302,7 +315,7 @@ class ChatRenderer {
|
||||
highlightRegex = new RegExp('(' + regexStr + ')', flags);
|
||||
} else {
|
||||
const pattern = `${matchWord ? '\\b' : ''}(${highlightWords.join(
|
||||
'|'
|
||||
'|',
|
||||
)})${matchWord ? '\\b' : ''}`;
|
||||
highlightRegex = new RegExp(pattern, flags);
|
||||
}
|
||||
@@ -361,7 +374,7 @@ class ChatRenderer {
|
||||
visibleMessageLimit,
|
||||
combineMessageLimit,
|
||||
combineIntervalLimit,
|
||||
exportLimit
|
||||
exportLimit,
|
||||
) {
|
||||
this.visibleMessageLimit = visibleMessageLimit;
|
||||
this.combineMessageLimit = combineMessageLimit;
|
||||
@@ -485,7 +498,7 @@ class ChatRenderer {
|
||||
<Element {...outputProps}>
|
||||
<span dangerouslySetInnerHTML={oldHtml} />
|
||||
</Element>,
|
||||
childNode
|
||||
childNode,
|
||||
);
|
||||
/* eslint-enable react/no-danger */
|
||||
}
|
||||
@@ -504,7 +517,7 @@ class ChatRenderer {
|
||||
node,
|
||||
parser.highlightRegex,
|
||||
parser.highlightWords,
|
||||
(text) => createHighlightNode(text, parser.highlightColor)
|
||||
(text) => createHighlightNode(text, parser.highlightColor),
|
||||
);
|
||||
if (highlighted && parser.highlightWholeMessage) {
|
||||
node.className += ' ChatMessage--highlighted';
|
||||
@@ -537,7 +550,7 @@ class ChatRenderer {
|
||||
!Byond.IS_LTE_IE8 &&
|
||||
MESSAGE_TYPES.find(
|
||||
(typeDef) =>
|
||||
typeDef.selector && node.querySelector(typeDef.selector)
|
||||
typeDef.selector && node.querySelector(typeDef.selector),
|
||||
);
|
||||
message.type = typeDef?.type || MESSAGE_TYPE_UNKNOWN;
|
||||
}
|
||||
@@ -598,7 +611,7 @@ class ChatRenderer {
|
||||
// Remove pruned messages from the message array
|
||||
|
||||
this.messages = this.messages.filter(
|
||||
(message) => message.node !== 'pruned'
|
||||
(message) => message.node !== 'pruned',
|
||||
);
|
||||
logger.log(`pruned ${fromIndex} visible messages`);
|
||||
}
|
||||
@@ -607,7 +620,7 @@ class ChatRenderer {
|
||||
{
|
||||
const fromIndex = Math.max(
|
||||
0,
|
||||
this.messages.length - this.persistentMessageLimit
|
||||
this.messages.length - this.persistentMessageLimit,
|
||||
);
|
||||
if (fromIndex > 0) {
|
||||
this.messages = this.messages.slice(fromIndex);
|
||||
|
||||
@@ -149,7 +149,7 @@ export const highlightNode = (
|
||||
node,
|
||||
regex,
|
||||
words,
|
||||
createNode = createHighlightNode
|
||||
createNode = createHighlightNode,
|
||||
) => {
|
||||
if (!createNode) {
|
||||
createNode = createHighlightNode;
|
||||
|
||||
@@ -82,14 +82,14 @@ const setupApp = () => {
|
||||
Byond.winset('browseroutput', {
|
||||
'is-visible': true,
|
||||
'is-disabled': false,
|
||||
'pos': '0x0',
|
||||
'size': '0x0',
|
||||
pos: '0x0',
|
||||
size: '0x0',
|
||||
});
|
||||
|
||||
// Resize the panel to match the non-browser output
|
||||
Byond.winget('output').then((output: { size: string }) => {
|
||||
Byond.winset('browseroutput', {
|
||||
'size': output.size,
|
||||
size: output.size,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -110,7 +110,7 @@ const setupApp = () => {
|
||||
],
|
||||
() => {
|
||||
renderApp();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
import { clamp01, scale } from 'common/math';
|
||||
|
||||
import { pingFail, pingSuccess } from './actions';
|
||||
import { PING_MAX_FAILS, PING_ROUNDTRIP_BEST, PING_ROUNDTRIP_WORST } from './constants';
|
||||
import {
|
||||
PING_MAX_FAILS,
|
||||
PING_ROUNDTRIP_BEST,
|
||||
PING_ROUNDTRIP_WORST,
|
||||
} from './constants';
|
||||
|
||||
type PingState = {
|
||||
roundtrip: number | undefined;
|
||||
@@ -36,7 +40,7 @@ export const pingReducer = (state = {} as PingState, action) => {
|
||||
if (type === pingFail.type) {
|
||||
const { failCount = 0 } = state;
|
||||
const networkQuality = clamp01(
|
||||
state.networkQuality - failCount / PING_MAX_FAILS
|
||||
state.networkQuality - failCount / PING_MAX_FAILS,
|
||||
);
|
||||
const nextState: PingState = {
|
||||
...state,
|
||||
|
||||
@@ -25,7 +25,8 @@ export const ReconnectButton = (props) => {
|
||||
color="white"
|
||||
onClick={() => {
|
||||
Byond.command('.reconnect');
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Reconnect
|
||||
</Button>
|
||||
<Button
|
||||
@@ -33,14 +34,16 @@ export const ReconnectButton = (props) => {
|
||||
onClick={() => {
|
||||
location.href = `byond://${url}`;
|
||||
Byond.command('.quit');
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Relaunch game
|
||||
</Button>
|
||||
<Button
|
||||
color="white"
|
||||
onClick={() => {
|
||||
dispatch(dismissWarning());
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -7,13 +7,42 @@
|
||||
import { toFixed } from 'common/math';
|
||||
import { useLocalState } from 'tgui/backend';
|
||||
import { useDispatch, useSelector } from 'tgui/backend';
|
||||
import { Box, Button, ColorBox, Divider, Dropdown, Flex, Input, LabeledList, NumberInput, Section, Stack, Tabs, TextArea } from 'tgui/components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ColorBox,
|
||||
Divider,
|
||||
Dropdown,
|
||||
Flex,
|
||||
Input,
|
||||
LabeledList,
|
||||
NumberInput,
|
||||
Section,
|
||||
Stack,
|
||||
Tabs,
|
||||
TextArea,
|
||||
} from 'tgui/components';
|
||||
import { ChatPageSettings } from '../chat';
|
||||
import { rebuildChat, saveChatToDisk, purgeChatMessageArchive } from '../chat/actions';
|
||||
import {
|
||||
rebuildChat,
|
||||
saveChatToDisk,
|
||||
purgeChatMessageArchive,
|
||||
} from '../chat/actions';
|
||||
import { THEMES } from '../themes';
|
||||
import { changeSettingsTab, updateSettings, addHighlightSetting, removeHighlightSetting, updateHighlightSetting } from './actions';
|
||||
import {
|
||||
changeSettingsTab,
|
||||
updateSettings,
|
||||
addHighlightSetting,
|
||||
removeHighlightSetting,
|
||||
updateHighlightSetting,
|
||||
} from './actions';
|
||||
import { SETTINGS_TABS, FONTS, MAX_HIGHLIGHT_SETTINGS } from './constants';
|
||||
import { selectActiveTab, selectSettings, selectHighlightSettings, selectHighlightSettingById } from './selectors';
|
||||
import {
|
||||
selectActiveTab,
|
||||
selectSettings,
|
||||
selectHighlightSettings,
|
||||
selectHighlightSettingById,
|
||||
} from './selectors';
|
||||
|
||||
export const SettingsPanel = (props) => {
|
||||
const activeTab = useSelector(selectActiveTab);
|
||||
@@ -31,9 +60,10 @@ export const SettingsPanel = (props) => {
|
||||
dispatch(
|
||||
changeSettingsTab({
|
||||
tabId: tab.id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
{tab.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
@@ -67,7 +97,7 @@ export const SettingsGeneral = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
theme: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -83,7 +113,7 @@ export const SettingsGeneral = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
fontFamily: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -94,7 +124,7 @@ export const SettingsGeneral = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
fontFamily: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -127,7 +157,7 @@ export const SettingsGeneral = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
fontSize: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -145,7 +175,7 @@ export const SettingsGeneral = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
lineHeight: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -160,7 +190,7 @@ export const SettingsGeneral = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
showReconnectWarning: !showReconnectWarning,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -194,7 +224,7 @@ export const MessageLimits = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
visibleMessageLimit: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -212,7 +242,7 @@ export const MessageLimits = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
persistentMessageLimit: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -230,7 +260,7 @@ export const MessageLimits = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
combineMessageLimit: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -249,7 +279,7 @@ export const MessageLimits = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
combineIntervalLimit: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -296,7 +326,7 @@ export const ExportTab = (props) => {
|
||||
dispatch(
|
||||
updateSettings({
|
||||
logLineCount: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -316,7 +346,8 @@ export const ExportTab = (props) => {
|
||||
onClick={() => {
|
||||
dispatch(purgeChatMessageArchive());
|
||||
setPurgeConfirm(2);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{purgeConfirm > 1 ? 'Purged!' : 'Are you sure?'}
|
||||
</Button>
|
||||
) : (
|
||||
@@ -328,7 +359,8 @@ export const ExportTab = (props) => {
|
||||
setTimeout(() => {
|
||||
setPurgeConfirm(false);
|
||||
}, 5000);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Purge message archive
|
||||
</Button>
|
||||
)}
|
||||
@@ -402,7 +434,7 @@ const TextHighlightSetting = (props) => {
|
||||
dispatch(
|
||||
removeHighlightSetting({
|
||||
id: id,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -418,7 +450,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
highlightBlacklist: !highlightBlacklist,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -434,7 +466,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
highlightWholeMessage: !highlightWholeMessage,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -450,7 +482,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
matchWord: !matchWord,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -465,7 +497,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
matchCase: !matchCase,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -482,7 +514,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
highlightColor: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -497,7 +529,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
highlightText: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
@@ -511,7 +543,7 @@ const TextHighlightSetting = (props) => {
|
||||
updateHighlightSetting({
|
||||
id: id,
|
||||
blacklistText: value,
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -17,11 +17,11 @@ export const addHighlightSetting = createAction(
|
||||
'settings/addHighlightSetting',
|
||||
() => ({
|
||||
payload: createHighlightSetting(),
|
||||
})
|
||||
}),
|
||||
);
|
||||
export const removeHighlightSetting = createAction(
|
||||
'settings/removeHighlightSetting'
|
||||
'settings/removeHighlightSetting',
|
||||
);
|
||||
export const updateHighlightSetting = createAction(
|
||||
'settings/updateHighlightSetting'
|
||||
'settings/updateHighlightSetting',
|
||||
);
|
||||
|
||||
@@ -7,7 +7,13 @@
|
||||
import { storage } from 'common/storage';
|
||||
|
||||
import { setClientTheme } from '../themes';
|
||||
import { addHighlightSetting, loadSettings, removeHighlightSetting, updateHighlightSetting, updateSettings } from './actions';
|
||||
import {
|
||||
addHighlightSetting,
|
||||
loadSettings,
|
||||
removeHighlightSetting,
|
||||
updateHighlightSetting,
|
||||
updateSettings,
|
||||
} from './actions';
|
||||
import { FONTS_DISABLED } from './constants';
|
||||
import { selectSettings } from './selectors';
|
||||
|
||||
|
||||
@@ -4,7 +4,16 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { addHighlightSetting, changeSettingsTab, loadSettings, openChatSettings, removeHighlightSetting, toggleSettings, updateHighlightSetting, updateSettings } from './actions';
|
||||
import {
|
||||
addHighlightSetting,
|
||||
changeSettingsTab,
|
||||
loadSettings,
|
||||
openChatSettings,
|
||||
removeHighlightSetting,
|
||||
toggleSettings,
|
||||
updateHighlightSetting,
|
||||
updateSettings,
|
||||
} from './actions';
|
||||
import { FONTS, MAX_HIGHLIGHT_SETTINGS, SETTINGS_TABS } from './constants';
|
||||
import { createDefaultHighlightSetting } from './model';
|
||||
|
||||
@@ -139,7 +148,7 @@ export const settingsReducer = (state = initialState, action) => {
|
||||
} else {
|
||||
delete nextState.highlightSettingById[id];
|
||||
nextState.highlightSettings = nextState.highlightSettings.filter(
|
||||
(sid) => sid !== id
|
||||
(sid) => sid !== id,
|
||||
);
|
||||
if (!nextState.highlightSettings.length) {
|
||||
nextState.highlightSettings.push(defaultHighlightSetting.id);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
@use '~tgui/styles/base.scss' with (
|
||||
$color-bg: #202020,
|
||||
$color-bg-section: color.adjust(#202020, $lightness: -5%),
|
||||
$color-bg-grad-spread: 0%,
|
||||
$color-bg-grad-spread: 0%
|
||||
);
|
||||
|
||||
// Core styles
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
$color-fg: #000000,
|
||||
$color-bg: #eeeeee,
|
||||
$color-bg-section: #ffffff,
|
||||
$color-bg-grad-spread: 0%,
|
||||
$color-bg-grad-spread: 0%
|
||||
);
|
||||
|
||||
// A fat warning to anyone who wants to use this: this only half works.
|
||||
|
||||
@@ -7,23 +7,23 @@
|
||||
@use 'sass:meta';
|
||||
|
||||
@use '~tgui/styles/colors.scss' with (
|
||||
$primary: #ffffff,
|
||||
$bg-lightness: -25%,
|
||||
$fg-lightness: -10%,
|
||||
$label: #3b3b3b,
|
||||
// Makes button look actually grey due to weird maths.
|
||||
$grey: #ffffff,
|
||||
// Commenting out color maps will adjust all colors based on the lightness
|
||||
// settings above, but will add extra 10KB to the theme.
|
||||
// $fg-map-keys: (),
|
||||
// $bg-map-keys: (),
|
||||
);
|
||||
$primary: #ffffff,
|
||||
$bg-lightness: -25%,
|
||||
$fg-lightness: -10%,
|
||||
$label: #3b3b3b,
|
||||
// Makes button look actually grey due to weird maths.
|
||||
$grey: #ffffff,
|
||||
// Commenting out color maps will adjust all colors based on the lightness
|
||||
// settings above, but will add extra 10KB to the theme.
|
||||
// $fg-map-keys: (),
|
||||
// $bg-map-keys: (),
|
||||
);
|
||||
@use '~tgui/styles/base.scss' with (
|
||||
$color-fg: #000000,
|
||||
$color-bg: #eeeeee,
|
||||
$color-bg-section: #ffffff,
|
||||
$color-bg-grad-spread: 0%,
|
||||
);
|
||||
$color-fg: #000000,
|
||||
$color-bg: #eeeeee,
|
||||
$color-bg-section: #ffffff,
|
||||
$color-bg-grad-spread: 0%
|
||||
);
|
||||
|
||||
// A fat warning to anyone who wants to use this: this only half works.
|
||||
// It was made almost purely for the nuke ui, and requires a good amount of manual hacks to get it working as intended.
|
||||
|
||||
@@ -58,7 +58,7 @@ export const telemetryMiddleware = (store) => {
|
||||
let telemetryMutated = false;
|
||||
|
||||
const duplicateConnection = telemetry.connections.find((conn) =>
|
||||
connectionsMatch(conn, client)
|
||||
connectionsMatch(conn, client),
|
||||
);
|
||||
if (!duplicateConnection) {
|
||||
telemetryMutated = true;
|
||||
|
||||
@@ -229,7 +229,7 @@ export const backendMiddleware = (store) => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
logger.log(
|
||||
'visible in',
|
||||
perf.measure('render/finish', 'resume/finish')
|
||||
perf.measure('render/finish', 'resume/finish'),
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -328,7 +328,7 @@ type StateWithSetter<T> = [T, (nextState: T) => void];
|
||||
*/
|
||||
export const useLocalState = <T>(
|
||||
key: string,
|
||||
initialState: T
|
||||
initialState: T,
|
||||
): StateWithSetter<T> => {
|
||||
const state = globalStore?.getState()?.backend;
|
||||
const sharedStates = state?.shared ?? {};
|
||||
@@ -343,7 +343,7 @@ export const useLocalState = <T>(
|
||||
typeof nextState === 'function'
|
||||
? nextState(sharedState)
|
||||
: nextState,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
];
|
||||
@@ -365,7 +365,7 @@ export const useLocalState = <T>(
|
||||
*/
|
||||
export const useSharedState = <T>(
|
||||
key: string,
|
||||
initialState: T
|
||||
initialState: T,
|
||||
): StateWithSetter<T> => {
|
||||
const state = globalStore?.getState()?.backend;
|
||||
const sharedStates = state?.shared ?? {};
|
||||
@@ -378,7 +378,9 @@ export const useSharedState = <T>(
|
||||
key,
|
||||
value:
|
||||
JSON.stringify(
|
||||
typeof nextState === 'function' ? nextState(sharedState) : nextState
|
||||
typeof nextState === 'function'
|
||||
? nextState(sharedState)
|
||||
: nextState,
|
||||
) || '',
|
||||
});
|
||||
},
|
||||
|
||||
@@ -60,7 +60,8 @@ export class Blink extends Component {
|
||||
<span
|
||||
style={{
|
||||
visibility: this.state.hidden ? 'hidden' : 'visible',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -85,7 +85,8 @@ export class BodyZoneSelector extends Component<
|
||||
width: `${32 * scale}px`,
|
||||
height: `${32 * scale}px`,
|
||||
position: 'relative',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
as="img"
|
||||
src={resolveAsset(`body_zones.base_${theme}.png`)}
|
||||
|
||||
@@ -281,6 +281,6 @@ export const Box = (props: BoxProps) => {
|
||||
...computedProps,
|
||||
className: computedClassName,
|
||||
},
|
||||
children
|
||||
children,
|
||||
);
|
||||
};
|
||||
|
||||
@@ -85,7 +85,8 @@ export const Button = (props) => {
|
||||
return;
|
||||
}
|
||||
}}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
<div className="Button__content">
|
||||
{icon && iconPosition !== 'right' && (
|
||||
<Icon
|
||||
@@ -250,7 +251,8 @@ export class ButtonInput extends Component {
|
||||
'Button--color--' + color,
|
||||
])}
|
||||
{...rest}
|
||||
onClick={() => this.setInInput(true)}>
|
||||
onClick={() => this.setInInput(true)}
|
||||
>
|
||||
{icon && <Icon name={icon} rotation={iconRotation} spin={iconSpin} />}
|
||||
<div>{content}</div>
|
||||
<input
|
||||
|
||||
@@ -31,7 +31,7 @@ const normalizeData = (
|
||||
data: Point[],
|
||||
scale: number[],
|
||||
rangeX?: Range,
|
||||
rangeY?: Range
|
||||
rangeY?: Range,
|
||||
) => {
|
||||
if (data.length === 0) {
|
||||
return [];
|
||||
@@ -138,7 +138,8 @@ class LineChart extends Component<Props> {
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
overflow: 'hidden',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<polyline
|
||||
transform={`scale(1, -1) translate(0, -${viewBox[1]})`}
|
||||
fill={fillColor}
|
||||
|
||||
@@ -30,7 +30,8 @@ export class Collapsible extends Component {
|
||||
color={color}
|
||||
icon={open ? 'chevron-down' : 'chevron-right'}
|
||||
onClick={() => this.setState({ open: !open })}
|
||||
{...rest}>
|
||||
{...rest}
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,8 @@ export const ColorBox = (props) => {
|
||||
return (
|
||||
<div
|
||||
className={classes(['ColorBox', className, computeBoxClassName(rest)])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
{content || '.'}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -52,7 +52,8 @@ const DialogButton = (props: DialogButtonProps) => {
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className="Dialog__button"
|
||||
verticalAlignContent="middle">
|
||||
verticalAlignContent="middle"
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -96,13 +96,13 @@ export class DraggableControl extends Component {
|
||||
state.internalValue = clamp(
|
||||
state.internalValue + (offset * step) / stepPixelSize,
|
||||
minValue - step,
|
||||
maxValue + step
|
||||
maxValue + step,
|
||||
);
|
||||
// Clamp the final value
|
||||
state.value = clamp(
|
||||
state.internalValue - (state.internalValue % step) + stepOffset,
|
||||
minValue,
|
||||
maxValue
|
||||
maxValue,
|
||||
);
|
||||
state.origin = getScalarScreenOffset(e, dragMatrix);
|
||||
} else if (Math.abs(offset) > 4) {
|
||||
|
||||
@@ -88,7 +88,7 @@ export function Dropdown(props: Props) {
|
||||
const endIndex = options.length - 1;
|
||||
|
||||
let selectedIndex = options.findIndex(
|
||||
(option) => getOptionValue(option) === selected
|
||||
(option) => getOptionValue(option) === selected,
|
||||
);
|
||||
|
||||
if (selectedIndex < 0) {
|
||||
@@ -104,7 +104,7 @@ export function Dropdown(props: Props) {
|
||||
|
||||
onSelected?.(getOptionValue(options[newIndex]));
|
||||
},
|
||||
[disabled, onSelected, options, selected]
|
||||
[disabled, onSelected, options, selected],
|
||||
);
|
||||
|
||||
/** Allows the menu to be scrollable on open */
|
||||
@@ -123,7 +123,8 @@ export function Dropdown(props: Props) {
|
||||
<div
|
||||
className="Layout Dropdown__menu"
|
||||
style={{ minWidth: menuWidth }}
|
||||
ref={innerRef}>
|
||||
ref={innerRef}
|
||||
>
|
||||
{options.length === 0 && (
|
||||
<div className="Dropdown__menuentry">No options</div>
|
||||
)}
|
||||
@@ -141,13 +142,15 @@ export function Dropdown(props: Props) {
|
||||
onClick={() => {
|
||||
setOpen(false);
|
||||
onSelected?.(value);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{typeof option === 'string' ? option : option.displayText}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div className="Dropdown" style={{ width: unit(width) }}>
|
||||
<div
|
||||
@@ -165,7 +168,8 @@ export function Dropdown(props: Props) {
|
||||
}
|
||||
setOpen(!open);
|
||||
onClick?.(event);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{icon && (
|
||||
<Icon
|
||||
mr={1}
|
||||
@@ -178,7 +182,8 @@ export function Dropdown(props: Props) {
|
||||
className="Dropdown__selected-text"
|
||||
style={{
|
||||
overflow: clipSelectedText ? 'hidden' : 'visible',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{displayText || selected}
|
||||
</span>
|
||||
{!noChevron && (
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { Component, createRef, HTMLAttributes, PropsWithChildren, RefObject } from 'react';
|
||||
import {
|
||||
Component,
|
||||
createRef,
|
||||
HTMLAttributes,
|
||||
PropsWithChildren,
|
||||
RefObject,
|
||||
} from 'react';
|
||||
|
||||
const DEFAULT_ACCEPTABLE_DIFFERENCE = 5;
|
||||
|
||||
@@ -84,7 +90,8 @@ export class FitText extends Component<Props, State> {
|
||||
...(typeof this.props.native?.style === 'object'
|
||||
? this.props.native.style
|
||||
: {}),
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -87,7 +87,8 @@ export const IconStack = (props: IconStackProps) => {
|
||||
return (
|
||||
<span
|
||||
className={classes(['IconStack', className, computeBoxClassName(rest)])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -139,7 +139,8 @@ export class InfinitePlane extends Component {
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
},
|
||||
})}>
|
||||
})}
|
||||
>
|
||||
<div
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onMouseMove={this.handleMouseMove}
|
||||
@@ -162,7 +163,8 @@ export class InfinitePlane extends Component {
|
||||
transformOrigin: 'top left',
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -174,7 +176,8 @@ export class InfinitePlane extends Component {
|
||||
<ProgressBar
|
||||
minValue={ZOOM_MIN_VAL}
|
||||
value={zoom}
|
||||
maxValue={ZOOM_MAX_VAL}>
|
||||
maxValue={ZOOM_MAX_VAL}
|
||||
>
|
||||
{zoom}x
|
||||
</ProgressBar>
|
||||
</Stack.Item>
|
||||
|
||||
@@ -138,7 +138,8 @@ export class Input extends Component {
|
||||
monospace && 'Input--monospace',
|
||||
className,
|
||||
])}
|
||||
{...rest}>
|
||||
{...rest}
|
||||
>
|
||||
<div className="Input__baseline">.</div>
|
||||
<input
|
||||
ref={this.inputRef}
|
||||
|
||||
@@ -51,7 +51,8 @@ export const Knob = (props) => {
|
||||
suppressFlicker,
|
||||
unit,
|
||||
value,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{(control) => {
|
||||
const {
|
||||
dragging,
|
||||
@@ -65,7 +66,7 @@ export const Knob = (props) => {
|
||||
const scaledFillValue = scale(
|
||||
fillValue ?? displayValue,
|
||||
minValue,
|
||||
maxValue
|
||||
maxValue,
|
||||
);
|
||||
const scaledDisplayValue = scale(displayValue, minValue, maxValue);
|
||||
const effectiveColor =
|
||||
@@ -87,13 +88,15 @@ export const Knob = (props) => {
|
||||
},
|
||||
...rest,
|
||||
})}
|
||||
onMouseDown={handleDragStart}>
|
||||
onMouseDown={handleDragStart}
|
||||
>
|
||||
<div className="Knob__circle">
|
||||
<div
|
||||
className="Knob__cursorBox"
|
||||
style={{
|
||||
transform: `rotate(${rotation}deg)`,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div className="Knob__cursor" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -102,12 +105,14 @@ export const Knob = (props) => {
|
||||
)}
|
||||
<svg
|
||||
className="Knob__ring Knob__ringTrackPivot"
|
||||
viewBox="0 0 100 100">
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<circle className="Knob__ringTrack" cx="50" cy="50" r="50" />
|
||||
</svg>
|
||||
<svg
|
||||
className="Knob__ring Knob__ringFillPivot"
|
||||
viewBox="0 0 100 100">
|
||||
viewBox="0 0 100 100"
|
||||
>
|
||||
<circle
|
||||
className="Knob__ringFill"
|
||||
style={{
|
||||
@@ -115,7 +120,7 @@ export const Knob = (props) => {
|
||||
((bipolar ? 2.75 : 2.0) - scaledFillValue * 1.5) *
|
||||
Math.PI *
|
||||
50,
|
||||
0
|
||||
0,
|
||||
),
|
||||
}}
|
||||
cx="50"
|
||||
|
||||
@@ -14,7 +14,8 @@ export const LabeledControls = (props) => {
|
||||
wrap={wrap}
|
||||
align="stretch"
|
||||
justify="space-between"
|
||||
{...rest}>
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Flex>
|
||||
);
|
||||
@@ -30,7 +31,8 @@ const LabeledControlsItem = (props) => {
|
||||
align="center"
|
||||
textAlign="center"
|
||||
justify="space-between"
|
||||
{...rest}>
|
||||
{...rest}
|
||||
>
|
||||
<Flex.Item />
|
||||
<Flex.Item>{children}</Flex.Item>
|
||||
<Flex.Item color="label">{label}</Flex.Item>
|
||||
|
||||
@@ -60,7 +60,8 @@ const LabeledListItem = (props: LabeledListItemProps) => {
|
||||
as="span"
|
||||
style={{
|
||||
borderBottom: '2px dotted rgba(255, 255, 255, 0.8)',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{innerLabel}
|
||||
</Box>
|
||||
</Tooltip>
|
||||
@@ -76,7 +77,8 @@ const LabeledListItem = (props: LabeledListItemProps) => {
|
||||
// Kinda flipped because we want nowrap as default. Cleaner CSS this way though.
|
||||
!labelWrap && 'LabeledList__label--nowrap',
|
||||
])}
|
||||
verticalAlign={verticalAlign}>
|
||||
verticalAlign={verticalAlign}
|
||||
>
|
||||
{innerLabel}
|
||||
</Box>
|
||||
);
|
||||
@@ -90,7 +92,8 @@ const LabeledListItem = (props: LabeledListItemProps) => {
|
||||
textAlign={textAlign}
|
||||
className={classes(['LabeledList__cell', 'LabeledList__content'])}
|
||||
colSpan={buttons ? undefined : 2}
|
||||
verticalAlign={verticalAlign}>
|
||||
verticalAlign={verticalAlign}
|
||||
>
|
||||
{content}
|
||||
{children}
|
||||
</Box>
|
||||
@@ -114,7 +117,8 @@ const LabeledListDivider = (props: LabeledListDividerProps) => {
|
||||
style={{
|
||||
paddingTop: padding,
|
||||
paddingBottom: padding,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Divider />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -54,7 +54,8 @@ class Menu extends Component<MenuProps> {
|
||||
className={'MenuBar__menu'}
|
||||
style={{
|
||||
width: width,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -107,14 +108,16 @@ class MenuBarButton extends Component<MenuBarDropdownProps> {
|
||||
])}
|
||||
{...rest}
|
||||
onClick={disabled ? () => null : onClick}
|
||||
onMouseOver={onMouseOver}>
|
||||
onMouseOver={onMouseOver}
|
||||
>
|
||||
<span className="MenuBar__MenuBarButton-text">{display}</span>
|
||||
</Box>
|
||||
{open && (
|
||||
<Menu
|
||||
width={openWidth}
|
||||
menuRef={this.menuRef}
|
||||
onOutsideClick={onOutsideClick}>
|
||||
onOutsideClick={onOutsideClick}
|
||||
>
|
||||
{children}
|
||||
</Menu>
|
||||
)}
|
||||
@@ -170,7 +173,8 @@ export const Dropdown = (props: MenuBarItemProps) => {
|
||||
if (openOnHover) {
|
||||
setOpenMenuBar(entry);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</MenuBarButton>
|
||||
);
|
||||
@@ -186,7 +190,8 @@ const MenuItemToggle = (props) => {
|
||||
'MenuBar__MenuItemToggle',
|
||||
'MenuBar__hover',
|
||||
])}
|
||||
onClick={() => onClick(value)}>
|
||||
onClick={() => onClick(value)}
|
||||
>
|
||||
<div className="MenuBar__MenuItemToggle__check">
|
||||
{checked && <Icon size={1.3} name="check" />}
|
||||
</div>
|
||||
@@ -206,7 +211,8 @@ const MenuItem = (props) => {
|
||||
'MenuBar__MenuItem',
|
||||
'MenuBar__hover',
|
||||
])}
|
||||
onClick={() => onClick(value)}>
|
||||
onClick={() => onClick(value)}
|
||||
>
|
||||
{displayText}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,8 @@ export const Modal = (props) => {
|
||||
<Dimmer onKeyDown={handleKeyDown} /* VOREStation edit */>
|
||||
<div
|
||||
className={classes(['Modal', className, computeBoxClassName(rest)])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Dimmer>
|
||||
|
||||
@@ -132,13 +132,13 @@ export class NanoMap extends Component {
|
||||
height: mapSize,
|
||||
'margin-top': offsetY + 'px',
|
||||
'margin-left': offsetX + 'px',
|
||||
'overflow': 'hidden',
|
||||
'position': 'relative',
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
'background-image': 'url(' + mapUrl + ')',
|
||||
'background-size': 'cover',
|
||||
'background-repeat': 'no-repeat',
|
||||
'text-align': 'center',
|
||||
'cursor': dragging ? 'move' : 'auto',
|
||||
cursor: dragging ? 'move' : 'auto',
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -147,7 +147,8 @@ export class NanoMap extends Component {
|
||||
style={newStyle}
|
||||
textAlign="center"
|
||||
onMouseDown={this.handleDragStart}
|
||||
onClick={this.handleOnClick}>
|
||||
onClick={this.handleOnClick}
|
||||
>
|
||||
<Box>{children}</Box>
|
||||
</Box>
|
||||
<NanoMapZoomer zoom={zoom} onZoom={this.handleZoom} />
|
||||
@@ -176,7 +177,8 @@ const NanoMapMarker = (props, context) => {
|
||||
lineHeight="0"
|
||||
bottom={ry + 'px'}
|
||||
left={rx + 'px'}
|
||||
onMouseDown={handleOnClick}>
|
||||
onMouseDown={handleOnClick}
|
||||
>
|
||||
<Icon name={icon} color={color} fontSize="6px" />
|
||||
<Tooltip content={tooltip} />
|
||||
</Box>
|
||||
@@ -210,7 +212,7 @@ const NanoMapZoomer = (props) => {
|
||||
selected={~~level === ~~config.mapZLevel}
|
||||
content={level}
|
||||
onClick={() => {
|
||||
act('setZLevel', { 'mapZLevel': level });
|
||||
act('setZLevel', { mapZLevel: level });
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -40,7 +40,7 @@ export class NumberInput extends Component {
|
||||
this.setState({
|
||||
suppressingFlicker: false,
|
||||
}),
|
||||
suppressFlicker
|
||||
suppressFlicker,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -87,13 +87,13 @@ export class NumberInput extends Component {
|
||||
state.internalValue = clamp(
|
||||
state.internalValue + (offset * step) / stepPixelSize,
|
||||
minValue - step,
|
||||
maxValue + step
|
||||
maxValue + step,
|
||||
);
|
||||
// Clamp the final value
|
||||
state.value = clamp(
|
||||
state.internalValue - (state.internalValue % step) + stepOffset,
|
||||
minValue,
|
||||
maxValue
|
||||
maxValue,
|
||||
);
|
||||
state.origin = e.screenY;
|
||||
} else if (Math.abs(offset) > 4) {
|
||||
@@ -190,7 +190,8 @@ export class NumberInput extends Component {
|
||||
minHeight={height}
|
||||
lineHeight={lineHeight}
|
||||
fontSize={fontSize}
|
||||
onMouseDown={this.handleDragStart}>
|
||||
onMouseDown={this.handleDragStart}
|
||||
>
|
||||
<div className="NumberInput__barContainer">
|
||||
<div
|
||||
className="NumberInput__bar"
|
||||
@@ -199,7 +200,7 @@ export class NumberInput extends Component {
|
||||
clamp(
|
||||
((displayValue - minValue) / (maxValue - minValue)) * 100,
|
||||
0,
|
||||
100
|
||||
100,
|
||||
) + '%',
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { Placement } from '@popperjs/core';
|
||||
import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
PropsWithChildren,
|
||||
ReactNode,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { usePopper } from 'react-popper';
|
||||
|
||||
type RequiredProps = {
|
||||
@@ -29,7 +35,7 @@ export function Popper(props: PropsWithChildren<Props>) {
|
||||
const [referenceElement, setReferenceElement] =
|
||||
useState<HTMLDivElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(
|
||||
null
|
||||
null,
|
||||
);
|
||||
|
||||
// One would imagine we could just use useref here, but it's against react-popper documentation and causes a positioning bug
|
||||
@@ -69,7 +75,8 @@ export function Popper(props: PropsWithChildren<Props>) {
|
||||
ref={(node) => {
|
||||
setReferenceElement(node);
|
||||
parentRef.current = node;
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
{isOpen && (
|
||||
@@ -79,7 +86,8 @@ export function Popper(props: PropsWithChildren<Props>) {
|
||||
popperRef.current = node;
|
||||
}}
|
||||
style={{ ...styles.popper, zIndex: 5 }}
|
||||
{...attributes.popper}>
|
||||
{...attributes.popper}
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -47,7 +47,7 @@ export class RestrictedInput extends Component {
|
||||
e.target.value,
|
||||
minValue,
|
||||
maxValue,
|
||||
allowFloats
|
||||
allowFloats,
|
||||
);
|
||||
if (onChange) {
|
||||
onChange(e, +e.target.value);
|
||||
@@ -76,7 +76,7 @@ export class RestrictedInput extends Component {
|
||||
e.target.value,
|
||||
minValue,
|
||||
maxValue,
|
||||
allowFloats
|
||||
allowFloats,
|
||||
);
|
||||
this.setEditing(false);
|
||||
if (onChange) {
|
||||
@@ -110,7 +110,7 @@ export class RestrictedInput extends Component {
|
||||
nextValue,
|
||||
minValue,
|
||||
maxValue,
|
||||
allowFloats
|
||||
allowFloats,
|
||||
);
|
||||
}
|
||||
if (this.props.autoFocus || this.props.autoSelect) {
|
||||
@@ -136,7 +136,7 @@ export class RestrictedInput extends Component {
|
||||
nextValue,
|
||||
minValue,
|
||||
maxValue,
|
||||
allowFloats
|
||||
allowFloats,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,8 @@ export class RestrictedInput extends Component {
|
||||
monospace && 'Input--monospace',
|
||||
className,
|
||||
])}
|
||||
{...rest}>
|
||||
{...rest}
|
||||
>
|
||||
<div className="Input__baseline">.</div>
|
||||
<input
|
||||
className="Input__input"
|
||||
|
||||
@@ -72,14 +72,16 @@ export const RoundGauge = (props) => {
|
||||
...style,
|
||||
},
|
||||
...rest,
|
||||
})}>
|
||||
})}
|
||||
>
|
||||
<svg viewBox="0 0 100 50">
|
||||
{(alertAfter || alertBefore) && (
|
||||
<g
|
||||
className={classes([
|
||||
'RoundGauge__alert',
|
||||
alertColor ? `active RoundGauge__alert--${alertColor}` : '',
|
||||
])}>
|
||||
])}
|
||||
>
|
||||
<path d="M48.211,14.578C48.55,13.9 49.242,13.472 50,13.472C50.758,13.472 51.45,13.9 51.789,14.578C54.793,20.587 60.795,32.589 63.553,38.106C63.863,38.726 63.83,39.462 63.465,40.051C63.101,40.641 62.457,41 61.764,41C55.996,41 44.004,41 38.236,41C37.543,41 36.899,40.641 36.535,40.051C36.17,39.462 36.137,38.726 36.447,38.106C39.205,32.589 45.207,20.587 48.211,14.578ZM50,34.417C51.426,34.417 52.583,35.574 52.583,37C52.583,38.426 51.426,39.583 50,39.583C48.574,39.583 47.417,38.426 47.417,37C47.417,35.574 48.574,34.417 50,34.417ZM50,32.75C50,32.75 53,31.805 53,22.25C53,20.594 51.656,19.25 50,19.25C48.344,19.25 47,20.594 47,22.25C47,31.805 50,32.75 50,32.75Z" />
|
||||
</g>
|
||||
)}
|
||||
@@ -96,7 +98,7 @@ export const RoundGauge = (props) => {
|
||||
style={{
|
||||
strokeDashoffset: Math.max(
|
||||
(2.0 - (col_ranges[1] - col_ranges[0])) * Math.PI * 50,
|
||||
0
|
||||
0,
|
||||
),
|
||||
}}
|
||||
transform={`rotate(${180 + 180 * col_ranges[0]} 50 50)`}
|
||||
@@ -109,7 +111,8 @@ export const RoundGauge = (props) => {
|
||||
</g>
|
||||
<g
|
||||
className="RoundGauge__needle"
|
||||
transform={`rotate(${clampedValue * 180 - 90} 50 50)`}>
|
||||
transform={`rotate(${clampedValue * 180 - 90} 50 50)`}
|
||||
>
|
||||
<polygon
|
||||
className="RoundGauge__needleLine"
|
||||
points="46,50 50,0 54,50"
|
||||
|
||||
@@ -75,7 +75,8 @@ export const Section = (props: SectionProps) => {
|
||||
className,
|
||||
computeBoxClassName(rest),
|
||||
])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
{hasTitle && (
|
||||
<div className="Section__title">
|
||||
<span className="Section__titleText">{title}</span>
|
||||
@@ -89,7 +90,8 @@ export const Section = (props: SectionProps) => {
|
||||
'Section__content',
|
||||
!!stretchContents && 'Section__content--stretchContents', // VOREStation Addition
|
||||
!!noTopPadding && 'Section__content--noTopPadding', // VOREStation Addition
|
||||
])}>
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,7 +47,8 @@ export const Slider = (props) => {
|
||||
suppressFlicker,
|
||||
unit,
|
||||
value,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{(control) => {
|
||||
const {
|
||||
dragging,
|
||||
@@ -63,7 +64,7 @@ export const Slider = (props) => {
|
||||
const scaledFillValue = scale(
|
||||
fillValue ?? displayValue,
|
||||
minValue,
|
||||
maxValue
|
||||
maxValue,
|
||||
);
|
||||
const scaledDisplayValue = scale(displayValue, minValue, maxValue);
|
||||
// prettier-ignore
|
||||
@@ -79,7 +80,8 @@ export const Slider = (props) => {
|
||||
computeBoxClassName(rest),
|
||||
])}
|
||||
{...computeBoxProps(rest)}
|
||||
onMouseDown={handleDragStart}>
|
||||
onMouseDown={handleDragStart}
|
||||
>
|
||||
<div
|
||||
className={classes([
|
||||
'ProgressBar__fill',
|
||||
@@ -102,7 +104,8 @@ export const Slider = (props) => {
|
||||
className="Slider__cursorOffset"
|
||||
style={{
|
||||
width: clamp01(scaledDisplayValue) * 100 + '%',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<div className="Slider__cursor" />
|
||||
<div className="Slider__pointer" />
|
||||
{dragging && (
|
||||
|
||||
@@ -7,7 +7,14 @@
|
||||
import { classes } from 'common/react';
|
||||
import { RefObject } from 'react';
|
||||
|
||||
import { computeFlexClassName, computeFlexItemClassName, computeFlexItemProps, computeFlexProps, FlexItemProps, FlexProps } from './Flex';
|
||||
import {
|
||||
computeFlexClassName,
|
||||
computeFlexItemClassName,
|
||||
computeFlexItemProps,
|
||||
computeFlexProps,
|
||||
FlexItemProps,
|
||||
FlexProps,
|
||||
} from './Flex';
|
||||
|
||||
type Props = Partial<{
|
||||
vertical: boolean;
|
||||
|
||||
@@ -17,7 +17,8 @@ export const Table = (props) => {
|
||||
className,
|
||||
computeBoxClassName(rest),
|
||||
])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
<tbody>{children}</tbody>
|
||||
</table>
|
||||
);
|
||||
|
||||
@@ -44,7 +44,8 @@ export const Tabs = (props: Props) => {
|
||||
className,
|
||||
computeBoxClassName(rest),
|
||||
])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -72,7 +73,8 @@ const Tab = (props: TabProps) => {
|
||||
className,
|
||||
computeBoxClassName(rest),
|
||||
])}
|
||||
{...computeBoxProps(rest)}>
|
||||
{...computeBoxProps(rest)}
|
||||
>
|
||||
{(canRender(leftSlot) && <div className="Tab__left">{leftSlot}</div>) ||
|
||||
(!!icon && (
|
||||
<div className="Tab__left">
|
||||
|
||||
@@ -197,7 +197,8 @@ export class TextArea extends Component {
|
||||
noborder && 'TextArea--noborder',
|
||||
className,
|
||||
])}
|
||||
{...rest}>
|
||||
{...rest}
|
||||
>
|
||||
{!!displayedValue && (
|
||||
<Box position="absolute" width="100%" height="100%" overflow="hidden">
|
||||
<div
|
||||
@@ -207,7 +208,8 @@ export class TextArea extends Component {
|
||||
])}
|
||||
style={{
|
||||
transform: `translateY(-${scrolledAmount}px)`,
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{displayedValue}
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
@@ -117,7 +117,7 @@ export class Tooltip extends Component<TooltipProps, TooltipState> {
|
||||
{
|
||||
...DEFAULT_OPTIONS,
|
||||
placement: this.props.position || 'auto',
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
Tooltip.singletonPopper = singletonPopper;
|
||||
|
||||
@@ -91,94 +91,94 @@ export const CSS_COLORS = [
|
||||
// go use /client/verb/generate_tgui_radio_constants() in communications.dm.
|
||||
export const RADIO_CHANNELS = [
|
||||
{
|
||||
'name': 'Mercenary',
|
||||
'freq': 1213,
|
||||
'color': '#6D3F40',
|
||||
name: 'Mercenary',
|
||||
freq: 1213,
|
||||
color: '#6D3F40',
|
||||
},
|
||||
{
|
||||
'name': 'Raider',
|
||||
'freq': 1277,
|
||||
'color': '#6D3F40',
|
||||
name: 'Raider',
|
||||
freq: 1277,
|
||||
color: '#6D3F40',
|
||||
},
|
||||
{
|
||||
'name': 'Special Ops',
|
||||
'freq': 1341,
|
||||
'color': '#5C5C8A',
|
||||
name: 'Special Ops',
|
||||
freq: 1341,
|
||||
color: '#5C5C8A',
|
||||
},
|
||||
{
|
||||
'name': 'AI Private',
|
||||
'freq': 1343,
|
||||
'color': '#FF00FF',
|
||||
name: 'AI Private',
|
||||
freq: 1343,
|
||||
color: '#FF00FF',
|
||||
},
|
||||
{
|
||||
'name': 'Response Team',
|
||||
'freq': 1345,
|
||||
'color': '#5C5C8A',
|
||||
name: 'Response Team',
|
||||
freq: 1345,
|
||||
color: '#5C5C8A',
|
||||
},
|
||||
{
|
||||
'name': 'Supply',
|
||||
'freq': 1347,
|
||||
'color': '#5F4519',
|
||||
name: 'Supply',
|
||||
freq: 1347,
|
||||
color: '#5F4519',
|
||||
},
|
||||
{
|
||||
'name': 'Service',
|
||||
'freq': 1349,
|
||||
'color': '#6eaa2c',
|
||||
name: 'Service',
|
||||
freq: 1349,
|
||||
color: '#6eaa2c',
|
||||
},
|
||||
{
|
||||
'name': 'Science',
|
||||
'freq': 1351,
|
||||
'color': '#993399',
|
||||
name: 'Science',
|
||||
freq: 1351,
|
||||
color: '#993399',
|
||||
},
|
||||
{
|
||||
'name': 'Command',
|
||||
'freq': 1353,
|
||||
'color': '#193A7A',
|
||||
name: 'Command',
|
||||
freq: 1353,
|
||||
color: '#193A7A',
|
||||
},
|
||||
{
|
||||
'name': 'Medical',
|
||||
'freq': 1355,
|
||||
'color': '#008160',
|
||||
name: 'Medical',
|
||||
freq: 1355,
|
||||
color: '#008160',
|
||||
},
|
||||
{
|
||||
'name': 'Engineering',
|
||||
'freq': 1357,
|
||||
'color': '#A66300',
|
||||
name: 'Engineering',
|
||||
freq: 1357,
|
||||
color: '#A66300',
|
||||
},
|
||||
{
|
||||
'name': 'Security',
|
||||
'freq': 1359,
|
||||
'color': '#A30000',
|
||||
name: 'Security',
|
||||
freq: 1359,
|
||||
color: '#A30000',
|
||||
},
|
||||
{
|
||||
'name': 'Explorer',
|
||||
'freq': 1361,
|
||||
'color': '#555555',
|
||||
name: 'Explorer',
|
||||
freq: 1361,
|
||||
color: '#555555',
|
||||
},
|
||||
{
|
||||
'name': 'Talon',
|
||||
'freq': 1363,
|
||||
'color': '#555555',
|
||||
name: 'Talon',
|
||||
freq: 1363,
|
||||
color: '#555555',
|
||||
},
|
||||
{
|
||||
'name': 'Common',
|
||||
'freq': 1459,
|
||||
'color': '#008000',
|
||||
name: 'Common',
|
||||
freq: 1459,
|
||||
color: '#008000',
|
||||
},
|
||||
{
|
||||
'name': 'Entertainment',
|
||||
'freq': 1461,
|
||||
'color': '#339966',
|
||||
name: 'Entertainment',
|
||||
freq: 1461,
|
||||
color: '#339966',
|
||||
},
|
||||
{
|
||||
'name': 'Security(I)',
|
||||
'freq': 1475,
|
||||
'color': '#008000',
|
||||
name: 'Security(I)',
|
||||
freq: 1475,
|
||||
color: '#008000',
|
||||
},
|
||||
{
|
||||
'name': 'Medical(I)',
|
||||
'freq': 1485,
|
||||
'color': '#008000',
|
||||
name: 'Medical(I)',
|
||||
freq: 1485,
|
||||
color: '#008000',
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -187,58 +187,58 @@ Entries must match /code/defines/gases.dm entries.
|
||||
*/
|
||||
const GASES = [
|
||||
{
|
||||
'id': 'oxygen',
|
||||
'name': 'Oxygen',
|
||||
'label': 'O₂',
|
||||
'color': 'blue',
|
||||
id: 'oxygen',
|
||||
name: 'Oxygen',
|
||||
label: 'O₂',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
'id': 'nitrogen',
|
||||
'name': 'Nitrogen',
|
||||
'label': 'N₂',
|
||||
'color': 'green',
|
||||
id: 'nitrogen',
|
||||
name: 'Nitrogen',
|
||||
label: 'N₂',
|
||||
color: 'green',
|
||||
},
|
||||
{
|
||||
'id': 'carbon_dioxide',
|
||||
'name': 'Carbon Dioxide',
|
||||
'label': 'CO₂',
|
||||
'color': 'grey',
|
||||
id: 'carbon_dioxide',
|
||||
name: 'Carbon Dioxide',
|
||||
label: 'CO₂',
|
||||
color: 'grey',
|
||||
},
|
||||
{
|
||||
'id': 'phoron',
|
||||
'name': 'Phoron',
|
||||
'label': 'Phoron',
|
||||
'color': 'pink',
|
||||
id: 'phoron',
|
||||
name: 'Phoron',
|
||||
label: 'Phoron',
|
||||
color: 'pink',
|
||||
},
|
||||
{
|
||||
'id': 'volatile_fuel',
|
||||
'name': 'Volatile Fuel',
|
||||
'label': 'EXP',
|
||||
'color': 'teal',
|
||||
id: 'volatile_fuel',
|
||||
name: 'Volatile Fuel',
|
||||
label: 'EXP',
|
||||
color: 'teal',
|
||||
},
|
||||
{
|
||||
'id': 'nitrous_oxide',
|
||||
'name': 'Nitrous Oxide',
|
||||
'label': 'N₂O',
|
||||
'color': 'red',
|
||||
id: 'nitrous_oxide',
|
||||
name: 'Nitrous Oxide',
|
||||
label: 'N₂O',
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
'id': 'other',
|
||||
'name': 'Other',
|
||||
'label': 'Other',
|
||||
'color': 'white',
|
||||
id: 'other',
|
||||
name: 'Other',
|
||||
label: 'Other',
|
||||
color: 'white',
|
||||
},
|
||||
{
|
||||
'id': 'pressure',
|
||||
'name': 'Pressure',
|
||||
'label': 'Pressure',
|
||||
'color': 'average',
|
||||
id: 'pressure',
|
||||
name: 'Pressure',
|
||||
label: 'Pressure',
|
||||
color: 'average',
|
||||
},
|
||||
{
|
||||
'id': 'temperature',
|
||||
'name': 'Temperature',
|
||||
'label': 'Temperature',
|
||||
'color': 'yellow',
|
||||
id: 'temperature',
|
||||
name: 'Temperature',
|
||||
label: 'Temperature',
|
||||
color: 'yellow',
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -252,7 +252,7 @@ export const getGasLabel = (gasId: string, fallbackValue?: string) => {
|
||||
|
||||
const gasSearchId = gasId.toLowerCase();
|
||||
const gasSearchName = gasId.replace(/(^\w{1})|(\s+\w{1})/g, (letter) =>
|
||||
letter.toUpperCase()
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
|
||||
for (let idx = 0; idx < GASES.length; idx++) {
|
||||
@@ -272,7 +272,7 @@ export const getGasColor = (gasId: string) => {
|
||||
|
||||
const gasSearchId = gasId.toLowerCase();
|
||||
const gasSearchName = gasId.replace(/(^\w{1})|(\s+\w{1})/g, (letter) =>
|
||||
letter.toUpperCase()
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
|
||||
for (let idx = 0; idx < GASES.length; idx++) {
|
||||
@@ -292,7 +292,7 @@ export const getGasFromId = (gasId: string): Gas | undefined => {
|
||||
|
||||
const gasSearchId = gasId.toLowerCase();
|
||||
const gasSearchName = gasId.replace(/(^\w{1})|(\s+\w{1})/g, (letter) =>
|
||||
letter.toUpperCase()
|
||||
letter.toUpperCase(),
|
||||
);
|
||||
|
||||
for (let idx = 0; idx < GASES.length; idx++) {
|
||||
|
||||
@@ -38,7 +38,8 @@ export const KitchenSink = (props) => {
|
||||
key={i}
|
||||
color="transparent"
|
||||
selected={i === pageIndex}
|
||||
onClick={() => setPageIndex(i)}>
|
||||
onClick={() => setPageIndex(i)}
|
||||
>
|
||||
{story.meta.title}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
|
||||
@@ -8,7 +8,11 @@ import { KEY_BACKSPACE, KEY_F10, KEY_F11, KEY_F12 } from 'common/keycodes';
|
||||
|
||||
import { globalEvents } from '../events';
|
||||
import { acquireHotKey } from '../hotkeys';
|
||||
import { openExternalBrowser, toggleDebugLayout, toggleKitchenSink } from './actions';
|
||||
import {
|
||||
openExternalBrowser,
|
||||
toggleDebugLayout,
|
||||
toggleKitchenSink,
|
||||
} from './actions';
|
||||
|
||||
// prettier-ignore
|
||||
const relayedTypes = [
|
||||
|
||||
@@ -76,7 +76,7 @@ const getScreenSize = (): [number, number] => [
|
||||
export const touchRecents = (
|
||||
recents: string[],
|
||||
touchedItem: string,
|
||||
limit = 50
|
||||
limit = 50,
|
||||
): [string[], string | undefined] => {
|
||||
const nextRecents: string[] = [touchedItem];
|
||||
let trimmedItem: string | undefined;
|
||||
@@ -105,7 +105,7 @@ const storeWindowGeometry = async () => {
|
||||
// Update the list of stored geometries
|
||||
const [geometries, trimmedKey] = touchRecents(
|
||||
(await storage.get('geometries')) || [],
|
||||
windowKey
|
||||
windowKey,
|
||||
);
|
||||
if (trimmedKey) {
|
||||
storage.remove(trimmedKey);
|
||||
@@ -120,7 +120,7 @@ export const recallWindowGeometry = async (
|
||||
pos?: [number, number];
|
||||
size?: [number, number];
|
||||
locked?: boolean;
|
||||
} = {}
|
||||
} = {},
|
||||
) => {
|
||||
const geometry = options.fancy && (await storage.get(windowKey));
|
||||
if (geometry) {
|
||||
@@ -157,7 +157,7 @@ export const recallWindowGeometry = async (
|
||||
pos = vecAdd(
|
||||
vecScale(areaAvailable, 0.5),
|
||||
vecScale(size, -0.5),
|
||||
vecScale(screenOffset, -1.0)
|
||||
vecScale(screenOffset, -1.0),
|
||||
);
|
||||
setWindowPosition(pos);
|
||||
}
|
||||
@@ -182,7 +182,7 @@ export const setupDrag = async () => {
|
||||
*/
|
||||
const constraintPosition = (
|
||||
pos: [number, number],
|
||||
size: [number, number]
|
||||
size: [number, number],
|
||||
): [boolean, [number, number]] => {
|
||||
const screenPos = getScreenPosition();
|
||||
const screenSize = getScreenSize();
|
||||
@@ -208,7 +208,7 @@ export const dragStartHandler = (event) => {
|
||||
dragging = true;
|
||||
dragPointOffset = vecSubtract(
|
||||
[event.screenX, event.screenY],
|
||||
getWindowPosition()
|
||||
getWindowPosition(),
|
||||
);
|
||||
// Focus click target
|
||||
(event.target as HTMLElement)?.focus();
|
||||
@@ -234,7 +234,7 @@ const dragMoveHandler = (event: MouseEvent) => {
|
||||
}
|
||||
event.preventDefault();
|
||||
setWindowPosition(
|
||||
vecSubtract([event.screenX, event.screenY], dragPointOffset)
|
||||
vecSubtract([event.screenX, event.screenY], dragPointOffset),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -246,7 +246,7 @@ export const resizeStartHandler =
|
||||
resizing = true;
|
||||
dragPointOffset = vecSubtract(
|
||||
[event.screenX, event.screenY],
|
||||
getWindowPosition()
|
||||
getWindowPosition(),
|
||||
);
|
||||
initialSize = getWindowSize();
|
||||
// Focus click target
|
||||
@@ -274,7 +274,7 @@ const resizeMoveHandler = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
const currentOffset = vecSubtract(
|
||||
[event.screenX, event.screenY],
|
||||
getWindowPosition()
|
||||
getWindowPosition(),
|
||||
);
|
||||
const delta = vecSubtract(currentOffset, dragPointOffset);
|
||||
// Extra 1x1 area is added to ensure the browser can see the cursor
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { addScrollableNode, canStealFocus, KeyEvent, removeScrollableNode, setupGlobalEvents } from './events';
|
||||
import {
|
||||
addScrollableNode,
|
||||
canStealFocus,
|
||||
KeyEvent,
|
||||
removeScrollableNode,
|
||||
setupGlobalEvents,
|
||||
} from './events';
|
||||
|
||||
describe('focusEvents', () => {
|
||||
afterEach(() => {
|
||||
|
||||
@@ -13,7 +13,7 @@ export const globalEvents = new EventEmitter();
|
||||
let ignoreWindowFocus = false;
|
||||
|
||||
export const setupGlobalEvents = (
|
||||
options: { ignoreWindowFocus?: boolean } = {}
|
||||
options: { ignoreWindowFocus?: boolean } = {},
|
||||
): void => {
|
||||
ignoreWindowFocus = !!options.ignoreWindowFocus;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { formatDb, formatMoney, formatSiBaseTenUnit, formatSiUnit, formatTime } from './format';
|
||||
import {
|
||||
formatDb,
|
||||
formatMoney,
|
||||
formatSiBaseTenUnit,
|
||||
formatSiUnit,
|
||||
formatTime,
|
||||
} from './format';
|
||||
|
||||
describe('formatSiUnit', () => {
|
||||
it('formats base values correctly', () => {
|
||||
|
||||
@@ -36,7 +36,7 @@ const SI_BASE_INDEX = SI_SYMBOLS.indexOf(' ');
|
||||
export const formatSiUnit = (
|
||||
value: number,
|
||||
minBase1000 = -SI_BASE_INDEX,
|
||||
unit = ''
|
||||
unit = '',
|
||||
): string => {
|
||||
if (!isFinite(value)) {
|
||||
return value.toString();
|
||||
@@ -123,7 +123,7 @@ const SI_BASE_TEN_UNITS = [
|
||||
export const formatSiBaseTenUnit = (
|
||||
value: number,
|
||||
minBase1000 = 0,
|
||||
unit = ''
|
||||
unit = '',
|
||||
): string => {
|
||||
if (!isFinite(value)) {
|
||||
return 'NaN';
|
||||
@@ -147,7 +147,7 @@ export const formatSiBaseTenUnit = (
|
||||
*/
|
||||
export const formatTime = (
|
||||
val: number,
|
||||
formatType: 'short' | 'default' = 'default'
|
||||
formatType: 'short' | 'default' = 'default',
|
||||
): string => {
|
||||
const totalSeconds = Math.floor(val / 10);
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
export const fetchRetry = (
|
||||
url: string,
|
||||
options?: RequestInit,
|
||||
retryTimer: number = 1000
|
||||
retryTimer: number = 1000,
|
||||
): Promise<Response> => {
|
||||
return fetch(url, options).catch(() => {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { Fragment } from 'react';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Dimmer, Icon, LabeledList, ProgressBar, Section } from '../components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Dimmer,
|
||||
Icon,
|
||||
LabeledList,
|
||||
ProgressBar,
|
||||
Section,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
|
||||
import { FullscreenNotice } from './common/FullscreenNotice';
|
||||
@@ -103,7 +111,8 @@ const ApcContent = (props) => {
|
||||
disabled={locked}
|
||||
onClick={() => act('breaker')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
[ {externalPowerStatus.externalPowerText} ]
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Power Cell">
|
||||
@@ -120,7 +129,8 @@ const ApcContent = (props) => {
|
||||
disabled={locked}
|
||||
onClick={() => act('charge')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
[ {chargingStatus.chargingText} ]
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
@@ -138,7 +148,8 @@ const ApcContent = (props) => {
|
||||
<Box
|
||||
inline
|
||||
mx={2}
|
||||
color={channel.status >= 2 ? 'good' : 'bad'}>
|
||||
color={channel.status >= 2 ? 'good' : 'bad'}
|
||||
>
|
||||
{channel.status >= 2 ? 'On' : 'Off'}
|
||||
</Box>
|
||||
<Button
|
||||
@@ -166,7 +177,8 @@ const ApcContent = (props) => {
|
||||
onClick={() => act('channel', topicParams.off)}
|
||||
/>
|
||||
</>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{channel.powerLoad} W
|
||||
</LabeledList.Item>
|
||||
);
|
||||
@@ -192,7 +204,8 @@ const ApcContent = (props) => {
|
||||
onClick={() => act('overload')}
|
||||
/>
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Cover Lock"
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { useBackend, useSharedState } from '../backend';
|
||||
import { Box, Button, LabeledList, Input, Section, Table, Tabs } from '../components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
LabeledList,
|
||||
Input,
|
||||
Section,
|
||||
Table,
|
||||
Tabs,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const AccountsTerminal = (props) => {
|
||||
@@ -42,19 +50,22 @@ const AccountTerminalContent = (props) => {
|
||||
<Tabs.Tab
|
||||
selected={!creating_new_account && !detailed_account_view}
|
||||
icon="home"
|
||||
onClick={() => act('view_accounts_list')}>
|
||||
onClick={() => act('view_accounts_list')}
|
||||
>
|
||||
Home
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={creating_new_account}
|
||||
icon="cog"
|
||||
onClick={() => act('create_account')}>
|
||||
onClick={() => act('create_account')}
|
||||
>
|
||||
New Account
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
disabled={creating_new_account}
|
||||
icon="print"
|
||||
onClick={() => act('print')}>
|
||||
onClick={() => act('print')}
|
||||
>
|
||||
Print
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
@@ -121,7 +132,8 @@ const DetailedView = (props) => {
|
||||
content="Suspend"
|
||||
onClick={() => act('toggle_suspension')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Account Number">
|
||||
#{account_number}
|
||||
@@ -201,13 +213,14 @@ const ListView = (props) => {
|
||||
<LabeledList.Item
|
||||
label={acc.owner_name + acc.suspended}
|
||||
color={acc.suspended ? 'bad' : null}
|
||||
key={acc.account_index}>
|
||||
key={acc.account_index}
|
||||
>
|
||||
<Button
|
||||
fluid
|
||||
content={'#' + acc.account_number}
|
||||
onClick={() =>
|
||||
act('view_account_detail', {
|
||||
'account_index': acc.account_index,
|
||||
account_index: acc.account_index,
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -66,7 +66,7 @@ export const ShuttleList = (props) => {
|
||||
<Section title="Overmap Ships">
|
||||
<Table>
|
||||
{sortBy((f: OvermapShip) => f.name?.toLowerCase() || f.name || f.ref)(
|
||||
overmap_ships
|
||||
overmap_ships,
|
||||
).map((ship) => (
|
||||
<Table.Row key={ship.ref}>
|
||||
<Table.Cell collapsing>
|
||||
|
||||
@@ -4,10 +4,10 @@ import { Box, Button, LabeledList, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const State = {
|
||||
'open': 'Open',
|
||||
'resolved': 'Resolved',
|
||||
'closed': 'Closed',
|
||||
'unknown': 'Unknown',
|
||||
open: 'Open',
|
||||
resolved: 'Resolved',
|
||||
closed: 'Closed',
|
||||
unknown: 'Unknown',
|
||||
};
|
||||
|
||||
type Data = {
|
||||
@@ -51,7 +51,8 @@ export const AdminTicketPanel = (props) => {
|
||||
/>{' '}
|
||||
<Button content="Legacy UI" onClick={() => act('legacy')} />
|
||||
</Box>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Admin Help Ticket">
|
||||
#{id}: <div dangerouslySetInnerHTML={{ __html: name }} />
|
||||
|
||||
@@ -38,7 +38,8 @@ export const AiAirlock = (props) => {
|
||||
content="Disrupt"
|
||||
onClick={() => act('disrupt-main')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{data.power.main ? 'Online' : 'Offline'}{' '}
|
||||
{((!data.wires.main_1 || !data.wires.main_2) &&
|
||||
'[Wires have been cut!]') ||
|
||||
@@ -55,7 +56,8 @@ export const AiAirlock = (props) => {
|
||||
content="Disrupt"
|
||||
onClick={() => act('disrupt-backup')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{data.power.backup ? 'Online' : 'Offline'}{' '}
|
||||
{((!data.wires.backup_1 || !data.wires.backup_2) &&
|
||||
'[Wires have been cut!]') ||
|
||||
@@ -86,7 +88,8 @@ export const AiAirlock = (props) => {
|
||||
onClick={() => act('shock-perm')}
|
||||
/>
|
||||
</>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{data.shock === 2 ? 'Safe' : 'Electrified'}{' '}
|
||||
{(!data.wires.shock && '[Wires have been cut!]') ||
|
||||
(data.shock_timeleft > 0 && `[${data.shock_timeleft}s]`) ||
|
||||
@@ -107,7 +110,8 @@ export const AiAirlock = (props) => {
|
||||
disabled={!data.wires.id_scanner}
|
||||
onClick={() => act('idscan-toggle')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{!data.wires.id_scanner && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
@@ -122,7 +126,8 @@ export const AiAirlock = (props) => {
|
||||
disabled={!data.wires.bolts}
|
||||
onClick={() => act('bolt-toggle')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{!data.wires.bolts && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
@@ -136,7 +141,8 @@ export const AiAirlock = (props) => {
|
||||
disabled={!data.wires.lights}
|
||||
onClick={() => act('light-toggle')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{!data.wires.lights && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
@@ -150,7 +156,8 @@ export const AiAirlock = (props) => {
|
||||
disabled={!data.wires.safe}
|
||||
onClick={() => act('safe-toggle')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{!data.wires.safe && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
@@ -164,7 +171,8 @@ export const AiAirlock = (props) => {
|
||||
disabled={!data.wires.timing}
|
||||
onClick={() => act('speed-toggle')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{!data.wires.timing && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
@@ -179,7 +187,8 @@ export const AiAirlock = (props) => {
|
||||
disabled={data.locked || data.welded}
|
||||
onClick={() => act('open-close')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{!!(data.locked || data.welded) && (
|
||||
<span>
|
||||
[Door is {data.locked ? 'bolted' : ''}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { Fragment } from 'react';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
LabeledList,
|
||||
NoticeBox,
|
||||
ProgressBar,
|
||||
Section,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const AiRestorer = () => {
|
||||
@@ -44,7 +51,8 @@ export const AiRestorerContent = (props) => {
|
||||
<Box inline bold color={isDead ? 'bad' : 'good'}>
|
||||
{isDead ? 'Nonfunctional' : 'Functional'}
|
||||
</Box>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Integrity">
|
||||
<ProgressBar
|
||||
|
||||
@@ -64,7 +64,8 @@ const AiSupermatterContent = (props) => {
|
||||
bad: [5000, Infinity],
|
||||
average: [4000, 5000],
|
||||
good: [-Infinity, 4000],
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{ambient_temp} K
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
|
||||
@@ -25,7 +25,7 @@ export const AirAlarm = (props) => {
|
||||
const AirAlarmStatus = (props) => {
|
||||
const { data } = useBackend();
|
||||
const entries = (data.environment_data || []).filter(
|
||||
(entry) => entry.value >= 0.01
|
||||
(entry) => entry.value >= 0.01,
|
||||
);
|
||||
const dangerMap = {
|
||||
0: {
|
||||
@@ -53,7 +53,8 @@ const AirAlarmStatus = (props) => {
|
||||
<LabeledList.Item
|
||||
key={entry.name}
|
||||
label={getGasLabel(entry.name)}
|
||||
color={status.color}>
|
||||
color={status.color}
|
||||
>
|
||||
{toFixed(entry.value, 2)}
|
||||
{entry.unit}
|
||||
</LabeledList.Item>
|
||||
@@ -64,7 +65,8 @@ const AirAlarmStatus = (props) => {
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Area status"
|
||||
color={data.atmos_alarm || data.fire_alarm ? 'bad' : 'good'}>
|
||||
color={data.atmos_alarm || data.fire_alarm ? 'bad' : 'good'}
|
||||
>
|
||||
{(data.atmos_alarm && 'Atmosphere Alarm') ||
|
||||
(data.fire_alarm && 'Fire Alarm') ||
|
||||
'Nominal'}
|
||||
@@ -95,17 +97,17 @@ const AirAlarmUnlockedControl = (props) => {
|
||||
<Button
|
||||
selected={rcon === 1}
|
||||
content="Off"
|
||||
onClick={() => act('rcon', { 'rcon': 1 })}
|
||||
onClick={() => act('rcon', { rcon: 1 })}
|
||||
/>
|
||||
<Button
|
||||
selected={rcon === 2}
|
||||
content="Auto"
|
||||
onClick={() => act('rcon', { 'rcon': 2 })}
|
||||
onClick={() => act('rcon', { rcon: 2 })}
|
||||
/>
|
||||
<Button
|
||||
selected={rcon === 3}
|
||||
content="On"
|
||||
onClick={() => act('rcon', { 'rcon': 3 })}
|
||||
onClick={() => act('rcon', { rcon: 3 })}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Thermostat">
|
||||
@@ -157,7 +159,8 @@ const AirAlarmControl = (props) => {
|
||||
onClick={() => setScreen()}
|
||||
/>
|
||||
)
|
||||
}>
|
||||
}
|
||||
>
|
||||
<Component />
|
||||
</Section>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { KEY_ENTER, KEY_ESCAPE, KEY_LEFT, KEY_RIGHT, KEY_SPACE, KEY_TAB } from '../../common/keycodes';
|
||||
import {
|
||||
KEY_ENTER,
|
||||
KEY_ESCAPE,
|
||||
KEY_LEFT,
|
||||
KEY_RIGHT,
|
||||
KEY_SPACE,
|
||||
KEY_TAB,
|
||||
} from '../../common/keycodes';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Autofocus, Box, Button, Flex, Section, Stack } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
@@ -65,7 +72,8 @@ export const AlertModal = (props) => {
|
||||
e.preventDefault();
|
||||
onKey(KEY_INCREMENT);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Section fill>
|
||||
<Stack fill vertical>
|
||||
<Stack.Item grow m={1}>
|
||||
@@ -100,7 +108,8 @@ const ButtonDisplay = (props) => {
|
||||
direction={!swapped_buttons ? 'row-reverse' : 'row'}
|
||||
fill
|
||||
justify="space-around"
|
||||
wrap>
|
||||
wrap
|
||||
>
|
||||
{buttons?.map((button, index) =>
|
||||
!!large_buttons && buttons.length < 3 ? (
|
||||
<Flex.Item grow key={index}>
|
||||
@@ -118,7 +127,7 @@ const ButtonDisplay = (props) => {
|
||||
selected={selected === index}
|
||||
/>
|
||||
</Flex.Item>
|
||||
)
|
||||
),
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
@@ -144,7 +153,8 @@ const AlertButton = (props) => {
|
||||
pt={large_buttons ? 0.33 : 0}
|
||||
selected={selected}
|
||||
textAlign="center"
|
||||
width={!large_buttons && buttonWidth}>
|
||||
width={!large_buttons && buttonWidth}
|
||||
>
|
||||
{!large_buttons ? button : button.toUpperCase()}
|
||||
</Button>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, NoticeBox, LabeledList, ProgressBar, Section, Table } from '../components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
NoticeBox,
|
||||
LabeledList,
|
||||
ProgressBar,
|
||||
Section,
|
||||
Table,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { capitalize } from 'common/string';
|
||||
|
||||
@@ -36,7 +44,8 @@ export const AlgaeFarm = (props) => {
|
||||
selected={usePower === 2}
|
||||
onClick={() => act('toggle')}
|
||||
/>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Flow Rate">
|
||||
{last_flow_rate} L/s
|
||||
@@ -48,11 +57,13 @@ export const AlgaeFarm = (props) => {
|
||||
{materials.map((material) => (
|
||||
<LabeledList.Item
|
||||
key={material.name}
|
||||
label={capitalize(material.display)}>
|
||||
label={capitalize(material.display)}
|
||||
>
|
||||
<ProgressBar
|
||||
width="80%"
|
||||
value={material.qty}
|
||||
maxValue={material.max}>
|
||||
maxValue={material.max}
|
||||
>
|
||||
{material.qty}/{material.max}
|
||||
</ProgressBar>
|
||||
<Button
|
||||
|
||||
@@ -2,7 +2,16 @@ import { sortBy } from 'common/collections';
|
||||
import { capitalize, decodeHtmlEntities } from 'common/string';
|
||||
import { Fragment } from 'react';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, ByondUi, Flex, LabeledList, Section, Tabs, ColorBox } from '../components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
ByondUi,
|
||||
Flex,
|
||||
LabeledList,
|
||||
Section,
|
||||
Tabs,
|
||||
ColorBox,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const AppearanceChanger = (props) => {
|
||||
@@ -65,44 +74,52 @@ export const AppearanceChanger = (props) => {
|
||||
<LabeledList.Item label="Name">{name}</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Species"
|
||||
color={!change_race ? 'grey' : null}>
|
||||
color={!change_race ? 'grey' : null}
|
||||
>
|
||||
{specimen}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Biological Sex"
|
||||
color={!change_gender ? 'grey' : null}>
|
||||
color={!change_gender ? 'grey' : null}
|
||||
>
|
||||
{gender ? capitalize(gender) : 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Gender Identity"
|
||||
color={!change_color ? 'grey' : null}>
|
||||
color={!change_color ? 'grey' : null}
|
||||
>
|
||||
{gender_id ? capitalize(gender_id) : 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Hair Style"
|
||||
color={!change_hair ? 'grey' : null}>
|
||||
color={!change_hair ? 'grey' : null}
|
||||
>
|
||||
{hair_style ? capitalize(hair_style) : 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Facial Hair Style"
|
||||
color={!change_facial_hair ? 'grey' : null}>
|
||||
color={!change_facial_hair ? 'grey' : null}
|
||||
>
|
||||
{facial_hair_style
|
||||
? capitalize(facial_hair_style)
|
||||
: 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Ear Style"
|
||||
color={!change_hair ? 'grey' : null}>
|
||||
color={!change_hair ? 'grey' : null}
|
||||
>
|
||||
{ear_style ? capitalize(ear_style) : 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Tail Style"
|
||||
color={!change_hair ? 'grey' : null}>
|
||||
color={!change_hair ? 'grey' : null}
|
||||
>
|
||||
{tail_style ? capitalize(tail_style) : 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Wing Style"
|
||||
color={!change_hair ? 'grey' : null}>
|
||||
color={!change_hair ? 'grey' : null}
|
||||
>
|
||||
{wing_style ? capitalize(wing_style) : 'Not Set'}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
@@ -141,27 +158,32 @@ export const AppearanceChanger = (props) => {
|
||||
<>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 3}
|
||||
onClick={() => setTabIndex(3)}>
|
||||
onClick={() => setTabIndex(3)}
|
||||
>
|
||||
Hair
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 5}
|
||||
onClick={() => setTabIndex(5)}>
|
||||
onClick={() => setTabIndex(5)}
|
||||
>
|
||||
Ear
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 6}
|
||||
onClick={() => setTabIndex(6)}>
|
||||
onClick={() => setTabIndex(6)}
|
||||
>
|
||||
Tail
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 7}
|
||||
onClick={() => setTabIndex(7)}>
|
||||
onClick={() => setTabIndex(7)}
|
||||
>
|
||||
Wing
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 8}
|
||||
onClick={() => setTabIndex(8)}>
|
||||
onClick={() => setTabIndex(8)}
|
||||
>
|
||||
Markings
|
||||
</Tabs.Tab>
|
||||
</>
|
||||
@@ -224,7 +246,7 @@ const AppearanceChangerGender = (props) => {
|
||||
key={g.gender_key}
|
||||
selected={g.gender_key === gender}
|
||||
content={g.gender_name}
|
||||
onClick={() => act('gender', { 'gender': g.gender_key })}
|
||||
onClick={() => act('gender', { gender: g.gender_key })}
|
||||
/>
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
@@ -234,7 +256,7 @@ const AppearanceChangerGender = (props) => {
|
||||
key={g.gender_key}
|
||||
selected={g.gender_key === gender_id}
|
||||
content={g.gender_name}
|
||||
onClick={() => act('gender_id', { 'gender_id': g.gender_key })}
|
||||
onClick={() => act('gender_id', { gender_id: g.gender_key })}
|
||||
/>
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Flex, LabeledList, ProgressBar, Section } from '../components';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
LabeledList,
|
||||
ProgressBar,
|
||||
Section,
|
||||
} from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const ArcadeBattle = (props) => {
|
||||
@@ -38,7 +45,8 @@ export const ArcadeBattle = (props) => {
|
||||
good: [20, 31],
|
||||
average: [10, 20],
|
||||
bad: [-Infinity, 10],
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{playerHP}HP
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
@@ -51,7 +59,8 @@ export const ArcadeBattle = (props) => {
|
||||
purple: [11, Infinity],
|
||||
violet: [3, 11],
|
||||
bad: [-Infinity, 3],
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{playerMP}MP
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
@@ -69,7 +78,8 @@ export const ArcadeBattle = (props) => {
|
||||
good: [20, 31],
|
||||
average: [10, 20],
|
||||
bad: [-Infinity, 10],
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{enemyHP}HP
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
|
||||
@@ -22,7 +22,8 @@ export const AssemblyInfrared = (props) => {
|
||||
icon="power-off"
|
||||
fluid
|
||||
selected={on}
|
||||
onClick={() => act('state')}>
|
||||
onClick={() => act('state')}
|
||||
>
|
||||
{on ? 'On' : 'Off'}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
@@ -31,7 +32,8 @@ export const AssemblyInfrared = (props) => {
|
||||
icon="eye"
|
||||
fluid
|
||||
selected={visible}
|
||||
onClick={() => act('visible')}>
|
||||
onClick={() => act('visible')}
|
||||
>
|
||||
{visible ? 'Able to be seen' : 'Invisible'}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
|
||||
@@ -18,10 +18,12 @@ export const AssemblyProx = (props) => {
|
||||
<Button
|
||||
icon="stopwatch"
|
||||
selected={timing}
|
||||
onClick={() => act('timing')}>
|
||||
onClick={() => act('timing')}
|
||||
>
|
||||
{timing ? 'Counting Down' : 'Disabled'}
|
||||
</Button>
|
||||
}>
|
||||
}
|
||||
>
|
||||
<NumberInput
|
||||
animated
|
||||
fluid
|
||||
@@ -49,7 +51,8 @@ export const AssemblyProx = (props) => {
|
||||
mr={1}
|
||||
icon={scanning ? 'lock' : 'lock-open'}
|
||||
selected={scanning}
|
||||
onClick={() => act('scanning')}>
|
||||
onClick={() => act('scanning')}
|
||||
>
|
||||
{scanning ? 'ARMED' : 'Unarmed'}
|
||||
</Button>
|
||||
Movement sensor is active when armed!
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user