TGUI 5.0 Patch 2 (#15737)

This commit is contained in:
Selis
2024-02-08 16:00:24 +01:00
committed by GitHub
parent c66f6e03d5
commit 93f076a120
319 changed files with 4081 additions and 3339 deletions
+4 -1
View File
@@ -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,
-14
View File
@@ -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
+4 -1
View File
@@ -2,5 +2,8 @@
"name": "eslint",
"version": "7.32.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
"type": "commonjs",
"bin": {
"eslint": "./bin/eslint.js"
}
}
+20
View File
@@ -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`);
-20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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",
+6 -6
View File
@@ -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;
+3 -3
View File
@@ -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,
);
/**
+9 -2
View File
@@ -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);
});
+9 -9
View File
@@ -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 };
+3 -3
View File
@@ -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)),
);
}
};
+2 -1
View File
@@ -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
View File
@@ -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>,
);
}
+1 -1
View File
@@ -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) {
+3 -3
View File
@@ -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,
})
}),
)
}>
}
>
&laquo;
</Button>
<Button
@@ -71,9 +86,10 @@ export const ChatPageSettings = (props) => {
dispatch(
moveChatPageRight({
pageId: page.id,
})
}),
)
}>
}
>
&raquo;
</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>
))}
+4 -3
View File
@@ -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>
)}
+5 -3
View File
@@ -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>
))}
+25 -6
View File
@@ -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;
+12 -1
View File
@@ -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();
+26 -13
View File
@@ -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;
+4 -4
View File
@@ -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();
}
},
);
}
};
+6 -2
View File
@@ -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,
+6 -3
View File
@@ -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,
})
}),
)
}
/>
+3 -3
View File
@@ -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';
+11 -2
View File
@@ -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);
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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;
+7 -5
View File
@@ -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,
) || '',
});
},
+2 -1
View File
@@ -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`)}
+1 -1
View File
@@ -281,6 +281,6 @@ export const Box = (props: BoxProps) => {
...computedProps,
className: computedClassName,
},
children
children,
);
};
+4 -2
View File
@@ -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
+3 -2
View File
@@ -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>
+2 -1
View File
@@ -22,7 +22,8 @@ export const ColorBox = (props) => {
return (
<div
className={classes(['ColorBox', className, computeBoxClassName(rest)])}
{...computeBoxProps(rest)}>
{...computeBoxProps(rest)}
>
{content || '.'}
</div>
);
+2 -1
View File
@@ -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) {
+12 -7
View File
@@ -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 && (
+9 -2
View File
@@ -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>
);
+2 -1
View File
@@ -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>
+2 -1
View File
@@ -138,7 +138,8 @@ export class Input extends Component {
monospace && 'Input--monospace',
className,
])}
{...rest}>
{...rest}
>
<div className="Input__baseline">.</div>
<input
ref={this.inputRef}
+12 -7
View File
@@ -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>
+12 -6
View File
@@ -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>
);
+2 -1
View File
@@ -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>
+8 -6
View File
@@ -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,
) + '%',
}}
/>
+12 -4
View File
@@ -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"
+7 -4
View File
@@ -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"
+4 -2
View File
@@ -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>
+7 -4
View File
@@ -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 && (
+8 -1
View File
@@ -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;
+2 -1
View File
@@ -17,7 +17,8 @@ export const Table = (props) => {
className,
computeBoxClassName(rest),
])}
{...computeBoxProps(rest)}>
{...computeBoxProps(rest)}
>
<tbody>{children}</tbody>
</table>
);
+4 -2
View File
@@ -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">
+4 -2
View File
@@ -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>
+1 -1
View File
@@ -117,7 +117,7 @@ export class Tooltip extends Component<TooltipProps, TooltipState> {
{
...DEFAULT_OPTIONS,
placement: this.props.position || 'auto',
}
},
);
Tooltip.singletonPopper = singletonPopper;
+93 -93
View File
@@ -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++) {
+2 -1
View File
@@ -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>
))}
+5 -1
View File
@@ -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 = [
+9 -9
View File
@@ -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
+7 -1
View File
@@ -1,4 +1,10 @@
import { addScrollableNode, canStealFocus, KeyEvent, removeScrollableNode, setupGlobalEvents } from './events';
import {
addScrollableNode,
canStealFocus,
KeyEvent,
removeScrollableNode,
setupGlobalEvents,
} from './events';
describe('focusEvents', () => {
afterEach(() => {
+1 -1
View File
@@ -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;
};
+7 -1
View File
@@ -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', () => {
+3 -3
View File
@@ -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);
+1 -1
View File
@@ -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) => {
+19 -6
View File
@@ -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 }} />
+18 -9
View File
@@ -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' : ''}
+10 -2
View File
@@ -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>
+10 -7
View File
@@ -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>
);
+15 -5
View File
@@ -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>
);
+15 -4
View File
@@ -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>
+14 -4
View File
@@ -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