This commit is contained in:
LetterN
2021-10-28 17:34:52 +08:00
parent 49940c373e
commit f7b898a2a9
346 changed files with 13714 additions and 8493 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -4,7 +4,7 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
+1 -1
View File
@@ -4,7 +4,7 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eslint",
"version": "7.21.0-pnpify",
"version": "7.32.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
}
+2 -2
View File
@@ -1,5 +1,5 @@
# This file is automatically generated by PnPify.
# Manual changes will be lost!
# This file is automatically generated by @yarnpkg/sdks.
# Manual changes might be lost!
integrations:
- vscode
+1 -1
View File
@@ -4,7 +4,7 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
+1 -1
View File
@@ -4,7 +4,7 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
+1 -1
View File
@@ -4,7 +4,7 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
+65 -19
View File
@@ -4,15 +4,22 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
const moduleWrapper = tsserver => {
if (!process.versions.pnp) {
return tsserver;
}
const {isAbsolute} = require(`path`);
const pnpApi = require(`pnpapi`);
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
return `${locator.name}@${locator.reference}`;
}));
@@ -23,9 +30,9 @@ const moduleWrapper = tsserver => {
function toEditorPath(str) {
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
if (isAbsolute(str) && !str.match(/^\^zip:/) && (str.match(/\.zip\//) || str.match(/\$\$virtual\//))) {
if (isAbsolute(str) && !str.match(/^\^zip:/) && (str.match(/\.zip\//) || isVirtual(str))) {
// We also take the opportunity to turn virtual paths into physical ones;
// this makes is much easier to work with workspaces that list peer
// this makes it much easier to work with workspaces that list peer
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
// file instances instead of the real ones.
//
@@ -34,26 +41,49 @@ const moduleWrapper = tsserver => {
// with peer dep (otherwise jumping into react-dom would show resolution
// errors on react).
//
const resolved = pnpApi.resolveVirtual(str);
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
if (resolved) {
const locator = pnpApi.findPackageLocator(resolved);
if (locator && dependencyTreeRoots.has(`${locator.name}@${locator.reference}`)) {
str = resolved;
str = resolved;
}
}
str = str.replace(/\\/g, `/`)
str = str.replace(/^\/?/, `/`);
str = normalize(str);
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
// VSCode only adds it automatically for supported schemes,
// so we have to do it manually for the `zip` scheme.
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
//
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
//
if (str.match(/\.zip\//)) {
str = `${isVSCode ? `^` : ``}zip:${str}`;
switch (hostInfo) {
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
// VSCode only adds it automatically for supported schemes,
// so we have to do it manually for the `zip` scheme.
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
//
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
//
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;
// 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;
default: {
str = `zip:${str}`;
} break;
}
}
}
@@ -66,15 +96,29 @@ const moduleWrapper = tsserver => {
: str.replace(/^\^?zip:/, ``);
}
// Force enable 'allowLocalPluginLoads'
// TypeScript tries to resolve plugins using a path relative to itself
// which doesn't work when using the global cache
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
// 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() {
this.projectService.allowLocalPluginLoads = true;
return originalEnablePluginsWithOptions.apply(this, arguments);
};
// And here is the point where we hijack the VSCode <-> TS communications
// by adding ourselves in the middle. We locate everything that looks
// like an absolute path of ours and normalize it.
const Session = tsserver.server.Session;
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
let isVSCode = false;
let hostInfo = `unknown`;
return Object.assign(Session.prototype, {
Object.assign(Session.prototype, {
onMessage(/** @type {string} */ message) {
const parsedMessage = JSON.parse(message)
@@ -82,9 +126,9 @@ const moduleWrapper = tsserver => {
parsedMessage != null &&
typeof parsedMessage === `object` &&
parsedMessage.arguments &&
parsedMessage.arguments.hostInfo === `vscode`
typeof parsedMessage.arguments.hostInfo === `string`
) {
isVSCode = true;
hostInfo = parsedMessage.arguments.hostInfo;
}
return originalOnMessage.call(this, JSON.stringify(parsedMessage, (key, value) => {
@@ -98,6 +142,8 @@ const moduleWrapper = tsserver => {
})));
}
});
return tsserver;
};
if (existsSync(absPnpApiPath)) {
+157
View File
@@ -0,0 +1,157 @@
#!/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);
const moduleWrapper = tsserver => {
if (!process.versions.pnp) {
return tsserver;
}
const {isAbsolute} = require(`path`);
const pnpApi = require(`pnpapi`);
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
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
// before forwarding it to TS, and to add it back on all returned paths.
function toEditorPath(str) {
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
if (isAbsolute(str) && !str.match(/^\^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
// file instances instead of the real ones.
//
// We only do this to modules owned by the the dependency tree roots.
// This avoids breaking the resolution when jumping inside a vendor
// with peer dep (otherwise jumping into react-dom would show resolution
// errors on react).
//
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
if (resolved) {
const locator = pnpApi.findPackageLocator(resolved);
if (locator && dependencyTreeRoots.has(`${locator.name}@${locator.reference}`)) {
str = resolved;
}
}
str = normalize(str);
if (str.match(/\.zip\//)) {
switch (hostInfo) {
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
// VSCode only adds it automatically for supported schemes,
// so we have to do it manually for the `zip` scheme.
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
//
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
//
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;
// 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;
default: {
str = `zip:${str}`;
} break;
}
}
}
return str;
}
function fromEditorPath(str) {
return process.platform === `win32`
? str.replace(/^\^?zip:\//, ``)
: str.replace(/^\^?zip:/, ``);
}
// Force enable 'allowLocalPluginLoads'
// TypeScript tries to resolve plugins using a path relative to itself
// which doesn't work when using the global cache
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
// 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() {
this.projectService.allowLocalPluginLoads = true;
return originalEnablePluginsWithOptions.apply(this, arguments);
};
// And here is the point where we hijack the VSCode <-> TS communications
// by adding ourselves in the middle. We locate everything that looks
// like an absolute path of ours and normalize it.
const Session = tsserver.server.Session;
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
let hostInfo = `unknown`;
Object.assign(Session.prototype, {
onMessage(/** @type {string} */ message) {
const parsedMessage = JSON.parse(message)
if (
parsedMessage != null &&
typeof parsedMessage === `object` &&
parsedMessage.arguments &&
typeof parsedMessage.arguments.hostInfo === `string`
) {
hostInfo = parsedMessage.arguments.hostInfo;
}
return originalOnMessage.call(this, JSON.stringify(parsedMessage, (key, value) => {
return typeof value === `string` ? fromEditorPath(value) : value;
}));
},
send(/** @type {any} */ msg) {
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
return typeof value === `string` ? toEditorPath(value) : value;
})));
}
});
return tsserver;
};
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
require(absPnpApiPath).setup();
}
}
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
+1 -1
View File
@@ -4,7 +4,7 @@ const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const relPnpApiPath = "../../../../.pnp.cjs";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "typescript",
"version": "4.2.3-pnpify",
"version": "4.3.5-sdk",
"main": "./lib/typescript.js",
"type": "commonjs"
}
+1 -1
View File
@@ -14,4 +14,4 @@ preferAggregateCacheInfo: true
preferInteractive: true
yarnPath: .yarn/releases/yarn-2.4.1.cjs
yarnPath: .yarn/releases/yarn-3.0.1.cjs
+1 -1
View File
@@ -14,7 +14,7 @@ const createBabelConfig = options => {
[require.resolve('@babel/preset-env'), {
modules: 'commonjs',
useBuiltIns: 'entry',
corejs: '3.10',
corejs: '3',
spec: false,
loose: true,
targets: [],
+2 -2
View File
@@ -59,7 +59,7 @@ task-webpack() {
## Runs a development server
task-dev-server() {
cd "${base_dir}"
yarn node packages/tgui-dev-server/index.esm.js "${@}"
yarn node --experimental-modules packages/tgui-dev-server/index.js "${@}"
}
## Run a linter through all packages
@@ -92,7 +92,7 @@ task-clean() {
rm -f .yarn/build-state.yml
rm -f .yarn/install-state.gz
rm -f .yarn/install-target
rm -f .pnp.js
rm -f .pnp.*
## NPM artifacts
rm -rf **/node_modules
rm -f **/package-lock.json
+2 -2
View File
@@ -47,7 +47,7 @@ function task-webpack {
## Runs a development server
function task-dev-server {
yarn node "packages/tgui-dev-server/index.esm.js" @Args
yarn node --experimental-modules "packages/tgui-dev-server/index.js" @Args
}
## Run a linter through all packages
@@ -75,7 +75,7 @@ function task-clean {
Remove-Quiet -Force ".yarn\build-state.yml"
Remove-Quiet -Force ".yarn\install-state.gz"
Remove-Quiet -Force ".yarn\install-target"
Remove-Quiet -Force ".pnp.js"
Remove-Quiet -Force ".pnp.*"
## NPM artifacts
Get-ChildItem -Path "." -Include "node_modules" -Recurse -File:$false | Remove-Item -Recurse -Force
Remove-Quiet -Force "package-lock.json"
+19 -13
View File
@@ -186,6 +186,7 @@ all available horizontal space.
- `bold: boolean` - Make text bold.
- `italic: boolean` - Make text italic.
- `nowrap: boolean` - Stops text from wrapping.
- `preserveWhitespace: boolean` - Preserves line-breaks and spacing in text.
- `textAlign: string` - Align text inside the box.
- `left` (default)
- `center`
@@ -220,11 +221,7 @@ Buttons allow users to take actions, and make choices, with a single click.
- `selected: boolean` - Activates the button (gives it a green color).
- `tooltip: string` - A fancy, boxy tooltip, which appears when hovering
over the button.
- `tooltipPosition: string` - Position of the tooltip.
- `top` - Show tooltip above the button.
- `bottom` (default) - Show tooltip below the button.
- `left` - Show tooltip on the left of the button.
- `right` - Show tooltip on the right of the button.
- `tooltipPosition?: string` - Position of the tooltip. See [`Popper`](#Popper) for valid options.
- `ellipsis: boolean` - If button width is constrained, button text will
be truncated with an ellipsis. Be careful however, because this prop breaks
the baseline alignment.
@@ -743,6 +740,16 @@ the input, or successfully enter a number.
- `onDrag: (e, value) => void` - An event, which fires about every 500ms
when you drag the input up and down, on release and on manual editing.
### `Popper`
Popper lets you position elements so that they don't go out of the bounds of the window. See [popper.js](https://popper.js.org/) for more information.
**Props:**
- `popperContent: InfernoNode` - The content that will be put inside the popper.
- `options?: { ... }` - An object of options to pass to `createPopper`. See [https://popper.js.org/docs/v2/constructors/#options], but the one you want most is `placement`. Valid placements are "bottom", "top", "left", and "right". You can affix "-start" and "-end" to achieve something like top left or top right respectively. You can also use "auto" (with an optional "-start" or "-end"), where a best fit will be chosen.
- `additionalStyles: { ... }` - A map of CSS styles to add to the element that will contain the popper.
### `ProgressBar`
Progress indicators inform users about the status of ongoing processes.
@@ -1092,17 +1099,16 @@ it is recommended to use that prop instead.
Usage:
```jsx
<Box position="relative">
Sample text.
<Tooltip
position="bottom"
content="Box tooltip" />
</Box>
<Tooltip position="bottom" content="Box tooltip">
<Box position="relative">
Sample text.
</Box>
</Tooltip>
```
**Props:**
- `position: string` - Tooltip position.
- `position?: string` - Tooltip position. See [`Popper`](#Popper) for valid options. Defaults to "auto".
- `content: string` - Content of the tooltip. Must be a plain string.
Fragments or other elements are **not** supported.
@@ -1133,7 +1139,7 @@ Example:
- `title: string` - Window title.
- `width: number` - Window width.
- `height: number` - Window height.
- `noClose: boolean` - Controls the ability to close the window.
- `canClose: boolean` - Controls the ability to close the window.
- `children: any` - Child elements, which are rendered directly inside the
window. If you use a [Dimmer](#dimmer) or [Modal](#modal) in your UI,
they should be put as direct childs of a Window, otherwise you should be
+28 -27
View File
@@ -6,39 +6,40 @@
"packages/*"
],
"dependencies": {
"@babel/core": "^7.13.15",
"@babel/eslint-parser": "^7.13.14",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-transform-jscript": "^7.12.13",
"@babel/preset-env": "^7.13.15",
"@babel/preset-typescript": "^7.13.0",
"@types/jest": "^26.0.22",
"@types/jsdom": "^16.2.10",
"@types/node": "^14.14.41",
"@typescript-eslint/parser": "^4.22.0",
"babel-jest": "^26.6.3",
"@babel/core": "^7.15.0",
"@babel/eslint-parser": "^7.15.0",
"@babel/plugin-proposal-class-properties": "^7.14.5",
"@babel/plugin-transform-jscript": "^7.14.5",
"@babel/preset-env": "^7.15.0",
"@babel/preset-typescript": "^7.15.0",
"@types/jest": "^27.0.1",
"@types/jsdom": "^16.2.13",
"@types/node": "^14.17.9",
"@typescript-eslint/parser": "^4.29.1",
"babel-jest": "^27.0.6",
"babel-loader": "^8.2.2",
"babel-plugin-inferno": "^6.2.0",
"babel-plugin-inferno": "^6.3.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"common": "workspace:*",
"css-loader": "^5.2.2",
"eslint": "^7.24.0",
"css-loader": "^5.2.7",
"eslint": "^7.32.0",
"eslint-plugin-radar": "^0.2.1",
"eslint-plugin-react": "^7.23.2",
"eslint-plugin-react": "^7.24.0",
"file-loader": "^6.2.0",
"inferno": "^7.4.8",
"jest": "^26.6.3",
"jest-circus": "^26.6.3",
"jsdom": "^16.5.3",
"mini-css-extract-plugin": "^1.4.1",
"sass": "^1.32.8",
"sass-loader": "^11.0.1",
"jest": "^27.0.6",
"jest-circus": "^27.0.6",
"jsdom": "^16.7.0",
"mini-css-extract-plugin": "^1.6.2",
"sass": "^1.37.5",
"sass-loader": "^11.1.1",
"style-loader": "^2.0.0",
"terser-webpack-plugin": "^5.1.1",
"typescript": "^4.2.4",
"terser-webpack-plugin": "^5.1.4",
"typescript": "^4.3.5",
"url-loader": "^4.1.1",
"webpack": "^5.33.2",
"webpack-bundle-analyzer": "^4.4.1",
"webpack-cli": "^4.6.0"
}
"webpack": "^5.50.0",
"webpack-bundle-analyzer": "^4.4.2",
"webpack-cli": "^4.7.2"
},
"packageManager": "yarn@3.0.1"
}
+1 -29
View File
@@ -1,29 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 437.4 434.4" style="enable-background:new 0 0 437.4 434.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:0.57;stroke-miterlimit:10;}
.st1{fill:none;stroke:#000000;stroke-width:0.5;stroke-miterlimit:10;}
.st2{stroke:#000000;stroke-width:18;stroke-miterlimit:10;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g id="Layer_3">
</g>
<path class="st0" d="M41.18,306.18l84.43,84.43l-19.53,20.8c-11.26,11.99-26.1,17.98-40.93,17.98c-14.84,0-29.68-5.99-40.93-17.98
c-22.52-23.97-22.52-63.2,0-87.18L41.18,306.18z"/>
<path class="st0" d="M351.66,149.88L139.65,375.65l-84.43-84.43L269.8,62.7c22.51-23.98,59.34-23.98,81.86,0
C374.17,86.67,374.17,125.9,351.66,149.88z"/>
<path class="st1" d="M426.11,88.46c-24.57-26.71-49.15-53.42-73.72-80.13c-5.87,0.15-12.84-0.21-20.56-1.73
c-3.76-0.74-7.22-1.66-10.37-2.66l0,0c-6.96,7.41-6.96,19.53,0,26.94l79.36,84.52c6.96,7.41,18.34,7.41,25.29,0
C433.07,107.99,433.07,95.87,426.11,88.46z"/>
<path class="st1" d="M248.07,35.83c13.66,16.01,4.9,48.19-19.57,71.87c-24.47,23.68-33.21,29.27-46.88,13.25
c-13.66-16.01-2.33-26.15,22.14-49.83C228.23,47.45,234.41,19.82,248.07,35.83z"/>
<line class="st0" x1="139.93" y1="375.93" x2="139.65" y2="375.65"/>
<line class="st0" x1="125.93" y1="390.93" x2="125.61" y2="390.61"/>
<rect x="191.43" y="-6.09" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -86.5159 210.9892)" class="st2" width="40" height="432.04"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 437.4 434.4" style="enable-background:new 0 0 437.4 434.4" xml:space="preserve"><style>.st0{fill:none;stroke:#000;stroke-width:.57;stroke-miterlimit:10}</style><g id="Layer_1"><path class="st0" d="m41.18 306.18 84.43 84.43-19.53 20.8c-11.26 11.99-26.1 17.98-40.93 17.98-14.84 0-29.68-5.99-40.93-17.98-22.52-23.97-22.52-63.2 0-87.18l16.96-18.05zm310.48-156.3L139.65 375.65l-84.43-84.43L269.8 62.7c22.51-23.98 59.34-23.98 81.86 0 22.51 23.97 22.51 63.2 0 87.18z"/><path d="M426.11 88.46c-24.57-26.71-49.15-53.42-73.72-80.13a93.834 93.834 0 0 1-30.93-4.39c-6.96 7.41-6.96 19.53 0 26.94l79.36 84.52c6.96 7.41 18.34 7.41 25.29 0 6.96-7.41 6.96-19.53 0-26.94zM248.07 35.83c13.66 16.01 4.9 48.19-19.57 71.87-24.47 23.68-33.21 29.27-46.88 13.25-13.66-16.01-2.33-26.15 22.14-49.83 24.47-23.67 30.65-51.3 44.31-35.29z" style="stroke-width:.5;fill:none;stroke:#000;stroke-miterlimit:10"/><path class="st0" d="m139.93 375.93-.28-.28m-13.72 15.28-.32-.32"/><path transform="rotate(-45.001 211.427 209.932)" style="stroke:#000;stroke-width:18;stroke-miterlimit:10" d="M191.43-6.09h40v432.04h-40z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+1 -26
View File
@@ -1,26 +1 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 437.4 434.4" style="enable-background:new 0 0 437.4 434.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#000000;stroke-width:0.57;stroke-miterlimit:10;}
.st1{fill:none;stroke:#000000;stroke-width:0.5;stroke-miterlimit:10;}
</style>
<g id="Layer_2">
</g>
<g id="Layer_1">
<g id="Layer_3">
</g>
<path class="st0" d="M41.18,306.18l84.43,84.43l-19.53,20.8c-11.26,11.99-26.1,17.98-40.93,17.98c-14.84,0-29.68-5.99-40.93-17.98
c-22.52-23.97-22.52-63.2,0-87.18L41.18,306.18z"/>
<path class="st0" d="M351.66,149.88L139.65,375.65l-84.43-84.43L269.8,62.7c22.51-23.98,59.34-23.98,81.86,0
C374.17,86.67,374.17,125.9,351.66,149.88z"/>
<path class="st1" d="M426.11,88.46c-24.57-26.71-49.15-53.42-73.72-80.13c-5.87,0.15-12.84-0.21-20.56-1.73
c-3.76-0.74-7.22-1.66-10.37-2.66l0,0c-6.96,7.41-6.96,19.53,0,26.94l79.36,84.52c6.96,7.41,18.34,7.41,25.29,0
C433.07,107.99,433.07,95.87,426.11,88.46z"/>
<path class="st1" d="M248.07,35.83c13.66,16.01,4.9,48.19-19.57,71.87c-24.47,23.68-33.21,29.27-46.88,13.25
c-13.66-16.01-2.33-26.15,22.14-49.83C228.23,47.45,234.41,19.82,248.07,35.83z"/>
<line class="st0" x1="139.93" y1="375.93" x2="139.65" y2="375.65"/>
<line class="st0" x1="125.93" y1="390.93" x2="125.61" y2="390.61"/>
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 437.4 434.4" style="enable-background:new 0 0 437.4 434.4" xml:space="preserve"><style>.st0{fill:none;stroke:#000;stroke-width:.57;stroke-miterlimit:10}</style><g id="Layer_1"><path class="st0" d="m41.18 306.18 84.43 84.43-19.53 20.8c-11.26 11.99-26.1 17.98-40.93 17.98-14.84 0-29.68-5.99-40.93-17.98-22.52-23.97-22.52-63.2 0-87.18l16.96-18.05zm310.48-156.3L139.65 375.65l-84.43-84.43L269.8 62.7c22.51-23.98 59.34-23.98 81.86 0 22.51 23.97 22.51 63.2 0 87.18z"/><path d="M426.11 88.46c-24.57-26.71-49.15-53.42-73.72-80.13a93.834 93.834 0 0 1-30.93-4.39c-6.96 7.41-6.96 19.53 0 26.94l79.36 84.52c6.96 7.41 18.34 7.41 25.29 0 6.96-7.41 6.96-19.53 0-26.94zM248.07 35.83c13.66 16.01 4.9 48.19-19.57 71.87-24.47 23.68-33.21 29.27-46.88 13.25-13.66-16.01-2.33-26.15 22.14-49.83 24.47-23.67 30.65-51.3 44.31-35.29z" style="stroke-width:.5;fill:none;stroke:#000;stroke-miterlimit:10"/><path class="st0" d="m139.93 375.93-.28-.28m-13.72 15.28-.32-.32"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1007 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="670" height="512"><path d="M655.714 228H484.286c-7.857 0-14.286 6.412-14.286 14.25v28.5c0 7.838 6.429 14.25 14.286 14.25h171.428c7.857 0 14.286-6.413 14.286-14.25v-28.5c0-7.838-6.429-14.25-14.286-14.25z" style="stroke-width:.89174"/><path d="M384 121.941V128H256V0h6.059a24 24 0 0 1 16.97 7.029l97.941 97.941a24.002 24.002 0 0 1 7.03 16.971zM248 160c-13.2 0-24-10.8-24-24V0H24C10.745 0 0 10.745 0 24v464c0 13.255 10.745 24 24 24h336c13.255 0 24-10.745 24-24V160zm-135.46 16c26.51 0 48 21.49 48 48s-21.49 48-48 48-48-21.49-48-48 21.491-48 48-48zm208 240h-256l.485-48.485L104.54 328c4.686-4.686 11.799-4.201 16.485.485L160.54 368l103.52-103.52c4.686-4.686 12.284-4.686 16.971 0l39.514 39.515z"/></svg>

After

Width:  |  Height:  |  Size: 746 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="670" height="512"><path d="M655.714 228.429h-57.143v-57.143c0-7.857-6.428-14.286-14.285-14.286h-28.572c-7.857 0-14.285 6.429-14.285 14.286v57.143h-57.143c-7.857 0-14.286 6.428-14.286 14.285v28.572c0 7.857 6.429 14.285 14.286 14.285h57.143v57.143c0 7.857 6.428 14.286 14.285 14.286h28.572c7.857 0 14.285-6.429 14.285-14.286v-57.143h57.143c7.857 0 14.286-6.428 14.286-14.285v-28.572c0-7.857-6.429-14.285-14.286-14.285z" style="stroke-width:.892857"/><path d="M384 121.941V128H256V0h6.059a24 24 0 0 1 16.97 7.029l97.941 97.941a24.002 24.002 0 0 1 7.03 16.971zM248 160c-13.2 0-24-10.8-24-24V0H24C10.745 0 0 10.745 0 24v464c0 13.255 10.745 24 24 24h336c13.255 0 24-10.745 24-24V160zm-135.46 16c26.51 0 48 21.49 48 48s-21.49 48-48 48-48-21.49-48-48 21.491-48 48-48zm208 240h-256l.485-48.485L104.54 328c4.686-4.686 11.799-4.201 16.485.485L160.54 368l103.52-103.52c4.686-4.686 12.284-4.686 16.971 0l39.514 39.515z"/></svg>

After

Width:  |  Height:  |  Size: 961 B

@@ -1,8 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 425 200" opacity=".33">
<path d="m 178.00399,0.03869 -71.20393,0 a 6.7613422,6.0255495 0 0 0 -6.76134,6.02555 l 0,187.87147 a 6.7613422,6.0255495 0 0 0 6.76134,6.02554 l 53.1072,0 a 6.7613422,6.0255495 0 0 0 6.76135,-6.02554 l 0,-101.544018 72.21628,104.699398 a 6.7613422,6.0255495 0 0 0 5.76015,2.87016 l 73.55487,0 a 6.7613422,6.0255495 0 0 0 6.76135,-6.02554 l 0,-187.87147 a 6.7613422,6.0255495 0 0 0 -6.76135,-6.02555 l -54.71644,0 a 6.7613422,6.0255495 0 0 0 -6.76133,6.02555 l 0,102.61935 L 183.76413,2.90886 a 6.7613422,6.0255495 0 0 0 -5.76014,-2.87017 z" />
<path d="M 4.8446333,22.10875 A 13.412039,12.501842 0 0 1 13.477588,0.03924 l 66.118315,0 a 5.3648158,5.000737 0 0 1 5.364823,5.00073 l 0,79.87931 z" />
<path d="m 420.15535,177.89119 a 13.412038,12.501842 0 0 1 -8.63295,22.06951 l -66.11832,0 a 5.3648152,5.000737 0 0 1 -5.36482,-5.00074 l 0,-79.87931 z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 425 200" opacity=".33"><path d="M178.004.039H106.8a6.761 6.026 0 0 0-6.761 6.025v187.872a6.761 6.026 0 0 0 6.761 6.025h53.107a6.761 6.026 0 0 0 6.762-6.025V92.392l72.216 104.7a6.761 6.026 0 0 0 5.76 2.87H318.2a6.761 6.026 0 0 0 6.761-6.026V6.064A6.761 6.026 0 0 0 318.2.04h-54.717a6.761 6.026 0 0 0-6.76 6.025v102.62L183.763 2.909a6.761 6.026 0 0 0-5.76-2.87zM4.845 22.109A13.412 12.502 0 0 1 13.478.039h66.118A5.365 5 0 0 1 84.96 5.04v79.88zm415.31 155.782a13.412 12.502 0 0 1-8.633 22.07h-66.118a5.365 5 0 0 1-5.365-5.001v-79.88z"/></svg>
<!-- This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. -->
<!-- http://creativecommons.org/licenses/by-sa/4.0/ -->

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 770 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="542" height="512"><path d="M215.03 71.05 126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97z"/><path d="M527.714 228H356.286c-7.857 0-14.286 6.412-14.286 14.25v28.5c0 7.838 6.429 14.25 14.286 14.25h171.428c7.857 0 14.286-6.413 14.286-14.25v-28.5c0-7.838-6.429-14.25-14.286-14.25z" style="stroke-width:.89174"/></svg>

After

Width:  |  Height:  |  Size: 469 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="542" height="512"><path d="M527.714 227.929h-57.143v-57.143c0-7.857-6.428-14.286-14.285-14.286h-28.572c-7.857 0-14.285 6.429-14.285 14.286v57.143h-57.143c-7.857 0-14.286 6.428-14.286 14.285v28.572c0 7.857 6.429 14.285 14.286 14.285h57.143v57.143c0 7.857 6.428 14.286 14.285 14.286h28.572c7.857 0 14.285-6.429 14.285-14.286v-57.143h57.143c7.857 0 14.286-6.428 14.286-14.285v-28.572c0-7.857-6.429-14.285-14.286-14.285z" style="stroke-width:.892857"/><path d="M215.03 71.05 126.06 160H24c-13.26 0-24 10.74-24 24v144c0 13.25 10.74 24 24 24h102.06l88.97 88.95c15.03 15.03 40.97 4.47 40.97-16.97V88.02c0-21.46-25.96-31.98-40.97-16.97z"/></svg>

After

Width:  |  Height:  |  Size: 684 B

@@ -1,6 +1,3 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 289.742" opacity=".33">
<path d="m 93.537677,0 c -18.113125,0 -34.220133,3.11164 -48.323484,9.33437 -13.965092,6.22167 -24.612442,15.07114 -31.940651,26.5471 -7.1899398,11.33789 -10.3012266,24.74911 -10.3012266,40.23478 0,10.64662 2.7250026,20.46465 8.1751116,29.45258 5.615277,8.98686 14.038277,17.35204 25.268821,25.09436 11.230544,7.60531 26.507421,15.41835 45.830514,23.43782 19.983748,8.29557 34.848848,15.55471 44.592998,21.77638 9.74414,6.22273 16.7617,12.8585 21.05572,19.90951 4.29404,7.05208 6.44193,15.76408 6.44193,26.13459 0,16.17702 -5.20196,28.48222 -15.60673,36.91682 -10.2396,8.4347 -25.02203,12.6523 -44.345169,12.6523 -14.038171,0 -25.515247,-1.6594 -34.433618,-4.9777 -8.91837,-3.4566 -16.185572,-8.7113 -21.800839,-15.7633 -5.615277,-7.0521 -10.074795,-16.66088 -13.377899,-28.82812 l -24.7731626293945,0 0,56.82632 C 33.856769,286.07601 63.74904,289.74201 89.678383,289.74201 c 16.020027,0 30.719787,-1.3827 44.097337,-4.1479 13.54272,-2.9043 25.1041,-7.4676 34.68309,-13.6893 9.74413,-6.3597 17.34042,-14.5195 22.79052,-24.4748 5.4501,-10.09332 8.17511,-22.39959 8.17511,-36.91682 0,-12.99764 -3.3021,-24.33539 -9.90829,-34.0146 -6.44105,-9.81725 -15.52545,-18.52707 -27.25146,-26.13133 -11.56085,-7.60427 -27.91083,-15.83142 -49.05066,-24.68022 -17.50644,-7.19012 -30.719668,-13.68948 -39.638038,-19.49701 -8.918371,-5.80752 -18.607474,-12.43409 -24.096524,-18.87417 -5.426043,-6.36616 -9.658826,-15.07003 -9.658826,-24.88729 0,-9.26401 2.075414,-17.21345 6.223454,-23.85033 11.098298,-14.39748 41.286638,-1.79507 45.075609,24.34762 4.839392,6.77491 8.84935,16.24729 12.029515,28.4156 l 20.53234,0 0,-55.99967 c -4.47825,-5.92448 -9.95488,-10.63222 -15.90837,-14.37411 1.64055,0.47905 3.19039,1.02376 4.63865,1.64024 6.49861,2.62607 12.16793,7.32747 17.0073,14.10345 4.83939,6.77491 8.84935,16.24567 12.02952,28.41397 0,0 8.48128,-0.12894 8.48978,-0.002 0.41776,6.41494 -1.75339,9.45286 -4.12342,12.56104 -2.4174,3.16978 -5.14486,6.78973 -4.00278,13.0029 1.50786,8.20318 10.18354,10.59642 14.62194,9.31154 -3.31842,-0.49911 -5.31855,-1.74948 -5.31855,-1.74948 0,0 1.87646,0.99868 5.65117,-1.35981 -3.27695,0.95571 -10.70529,-0.79738 -11.80125,-6.76313 -0.95752,-5.20861 0.94654,-7.29514 3.40113,-10.51482 2.45462,-3.21968 5.28426,-6.95831 4.6843,-14.48824 l 0.003,0.002 8.92676,0 0,-55.99967 c -15.07125,-3.87168 -27.65314,-6.36042 -37.74671,-7.46586 -9.95531,-1.10755 -20.18823,-1.65981 -30.696613,-1.65981 z m 70.321603,17.30893 0.23805,40.3049 c 1.31808,1.22666 2.43965,2.27815 3.34081,3.10602 4.83939,6.77491 8.84934,16.24566 12.02951,28.41397 l 20.53234,0 0,-55.99967 c -6.67731,-4.59381 -19.83643,-10.47309 -36.14071,-15.82522 z m -28.12049,5.60551 8.56479,17.71655 c -11.97037,-6.46697 -13.84678,-9.71726 -8.56479,-17.71655 z m 22.79705,0 c 2.7715,7.99929 1.78741,11.24958 -4.49354,17.71655 l 4.49354,-17.71655 z m 15.22195,24.00848 8.56479,17.71655 c -11.97038,-6.46697 -13.84679,-9.71726 -8.56479,-17.71655 z m 22.79704,0 c 2.7715,7.99929 1.78741,11.24958 -4.49354,17.71655 l 4.49354,-17.71655 z m -99.11384,2.20764 8.56479,17.71655 c -11.970382,-6.46697 -13.846782,-9.71726 -8.56479,-17.71655 z m 22.79542,0 c 2.7715,7.99929 1.78741,11.24958 -4.49354,17.71655 l 4.49354,-17.71655 z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" version="1.0" viewBox="0 0 200 289.742" opacity=".33"><path d="M93.538 0c-18.113 0-34.22 3.112-48.324 9.334-13.965 6.222-24.612 15.072-31.94 26.547C6.084 47.22 2.972 60.631 2.972 76.116c0 10.647 2.725 20.465 8.175 29.453 5.616 8.987 14.039 17.352 25.27 25.094 11.23 7.606 26.507 15.419 45.83 23.438 19.984 8.296 34.849 15.555 44.593 21.776 9.744 6.223 16.761 12.859 21.055 19.91 4.295 7.052 6.442 15.764 6.442 26.134 0 16.178-5.202 28.483-15.606 36.917-10.24 8.435-25.022 12.653-44.345 12.653-14.039 0-25.516-1.66-34.434-4.978-8.918-3.457-16.186-8.711-21.8-15.763-5.616-7.052-10.076-16.661-13.379-28.829H0v56.827c33.857 7.328 63.749 10.994 89.678 10.994 16.02 0 30.72-1.383 44.098-4.148 13.542-2.904 25.104-7.467 34.683-13.69 9.744-6.359 17.34-14.519 22.79-24.474 5.45-10.093 8.175-22.4 8.175-36.917 0-12.997-3.302-24.335-9.908-34.014-6.44-9.818-15.525-18.527-27.251-26.132-11.561-7.604-27.911-15.831-49.051-24.68-17.506-7.19-30.72-13.69-39.638-19.497S54.969 93.756 49.479 87.316c-5.426-6.366-9.658-15.07-9.658-24.887 0-9.264 2.075-17.214 6.223-23.85C57.142 24.18 87.331 36.782 91.12 62.925c4.84 6.775 8.85 16.247 12.03 28.415h20.532v-56c-4.479-5.924-9.955-10.631-15.909-14.373 1.64.479 3.19 1.023 4.639 1.64 6.498 2.626 12.168 7.327 17.007 14.103 4.84 6.775 8.85 16.246 12.03 28.414 0 0 8.48-.129 8.49-.002.417 6.415-1.754 9.453-4.124 12.561-2.417 3.17-5.145 6.79-4.003 13.003 1.508 8.203 10.184 10.597 14.622 9.312-3.318-.5-5.318-1.75-5.318-1.75s1.876.999 5.65-1.36c-3.276.956-10.704-.797-11.8-6.763-.958-5.208.946-7.295 3.4-10.514 2.455-3.22 5.285-6.959 4.685-14.489l.003.002h8.927v-56c-15.072-3.871-27.653-6.36-37.747-7.465C114.279.552 104.046 0 93.537 0zm70.321 17.309.238 40.305c1.318 1.226 2.44 2.278 3.341 3.106 4.84 6.775 8.85 16.246 12.03 28.414H200v-56c-6.677-4.594-19.836-10.473-36.14-15.825zm-28.12 5.605 8.565 17.717c-11.97-6.467-13.847-9.717-8.565-17.717zm22.797 0c2.771 8 1.787 11.25-4.494 17.717l4.494-17.717zm15.222 24.009 8.565 17.716c-11.97-6.466-13.847-9.717-8.565-17.716zm22.797 0c2.771 8 1.787 11.25-4.494 17.716l4.494-17.716zM97.44 49.13l8.565 17.716c-11.97-6.467-13.847-9.717-8.565-17.716zm22.795 0c2.772 7.999 1.788 11.25-4.493 17.716l4.493-17.716z"/></svg>
<!-- This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. -->
<!-- http://creativecommons.org/licenses/by-sa/4.0/ -->

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

+1 -1
View File
@@ -3,7 +3,7 @@
"name": "tgfont",
"version": "1.0.0",
"dependencies": {
"fantasticon": "^1.1.3"
"fantasticon": "^1.2.2"
},
"scripts": {
"build": "node mkdist.cjs && fantasticon --config config.cjs"
+3 -2
View File
@@ -4,11 +4,12 @@
* @license MIT
*/
import axios from 'axios';
import { exec } from 'child_process';
import { createLogger } from 'common/logging.js';
import { promisify } from 'util';
import { createLogger } from './logging.js';
import { require } from './require.js';
const axios = require('axios');
const logger = createLogger('dreamseeker');
const instanceByPid = new Map();
@@ -1,5 +0,0 @@
const esmRequire = require('esm')(module, {
cache: false,
});
esmRequire('./index.js');
@@ -37,7 +37,7 @@ if (process.env.NODE_ENV !== 'production') {
window.onunload = () => socket && socket.close();
}
export const subscribe = fn => subscribers.push(fn);
const subscribe = fn => subscribers.push(fn);
/**
* A json serializer which handles circular references and other junk.
@@ -91,7 +91,7 @@ const serializeObject = obj => {
return json;
};
export const sendMessage = msg => {
const sendMessage = msg => {
if (process.env.NODE_ENV !== 'production') {
const json = serializeObject(msg);
// Send message using WebSocket
@@ -119,7 +119,7 @@ export const sendMessage = msg => {
}
};
export const sendLogEntry = (level, ns, ...args) => {
const sendLogEntry = (level, ns, ...args) => {
if (process.env.NODE_ENV !== 'production') {
try {
sendMessage({
@@ -135,7 +135,7 @@ export const sendLogEntry = (level, ns, ...args) => {
}
};
export const setupHotReloading = () => {
const setupHotReloading = () => {
if (process.env.NODE_ENV !== 'production'
&& process.env.WEBPACK_HMR_ENABLED
&& window.WebSocket) {
@@ -168,3 +168,10 @@ export const setupHotReloading = () => {
}
}
};
module.exports = {
subscribe,
sendMessage,
sendLogEntry,
setupHotReloading,
};
@@ -4,13 +4,15 @@
* @license MIT
*/
import { createLogger } from 'common/logging.js';
import fs from 'fs';
import { basename } from 'path';
import SourceMap from 'source-map';
import { parse as parseStackTrace } from 'stacktrace-parser';
import { createLogger } from '../logging.js';
import { require } from '../require.js';
import { resolveGlob } from '../util.js';
const SourceMap = require('source-map');
const { parse: parseStackTrace } = require('stacktrace-parser');
const logger = createLogger('retrace');
const { SourceMapConsumer } = SourceMap;
+5 -3
View File
@@ -4,11 +4,13 @@
* @license MIT
*/
import { createLogger, directLog } from 'common/logging.js';
import http from 'http';
import { inspect } from 'util';
import WebSocket from 'ws';
import { retrace, loadSourceMaps } from './retrace.js';
import { createLogger, directLog } from '../logging.js';
import { require } from '../require.js';
import { loadSourceMaps, retrace } from './retrace.js';
const WebSocket = require('ws');
const logger = createLogger('link');
+3 -4
View File
@@ -2,13 +2,12 @@
"private": true,
"name": "tgui-dev-server",
"version": "4.3.0",
"type": "module",
"dependencies": {
"axios": "^0.21.1",
"common": "workspace:*",
"esm": "^3.2.25",
"glob": "^7.1.6",
"glob": "^7.1.7",
"source-map": "^0.7.3",
"stacktrace-parser": "^0.1.10",
"ws": "^7.4.6"
"ws": "^7.5.3"
}
}
+2 -2
View File
@@ -4,13 +4,13 @@
* @license MIT
*/
import { createLogger } from 'common/logging.js';
import fs from 'fs';
import os from 'os';
import { basename } from 'path';
import { DreamSeeker } from './dreamseeker.js';
import { createLogger } from './logging.js';
import { resolveGlob, resolvePath } from './util.js';
import { regQuery } from './winreg.js';
import { DreamSeeker } from './dreamseeker.js';
const logger = createLogger('reloader');
+9
View File
@@ -0,0 +1,9 @@
/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
*/
import { createRequire } from 'module';
export const require = createRequire(import.meta.url);
+11 -11
View File
@@ -4,26 +4,26 @@
* @license MIT
*/
import glob from 'glob';
import { resolve as resolvePath } from 'path';
import fs from 'fs';
import { promisify } from 'util';
import path from 'path';
import { require } from './require.js';
export { resolvePath };
const globPkg = require('glob');
export const resolvePath = path.resolve;
/**
* Combines path.resolve with glob patterns.
*/
export const resolveGlob = async (...sections) => {
const unsafePaths = await promisify(glob)(
resolvePath(...sections), {
strict: false,
silent: true,
});
export const resolveGlob = (...sections) => {
const unsafePaths = globPkg.sync(path.resolve(...sections), {
strict: false,
silent: true,
});
const safePaths = [];
for (let path of unsafePaths) {
try {
await promisify(fs.stat)(path);
fs.statSync(path);
safePaths.push(path);
}
catch {}
+1 -1
View File
@@ -4,11 +4,11 @@
* @license MIT
*/
import { createLogger } from 'common/logging.js';
import fs from 'fs';
import { createRequire } from 'module';
import { dirname } from 'path';
import { loadSourceMaps, setupLink } from './link/server.js';
import { createLogger } from './logging.js';
import { reloadByondCache } from './reloader.js';
import { resolveGlob } from './util.js';
+1 -1
View File
@@ -7,8 +7,8 @@
*/
import { exec } from 'child_process';
import { createLogger } from 'common/logging.js';
import { promisify } from 'util';
import { createLogger } from './logging.js';
const logger = createLogger('winreg');
+2 -2
View File
@@ -50,7 +50,7 @@ export const Panel = (props, context) => {
selected={audio.visible}
icon="music"
tooltip="Music player"
tooltipPosition="bottom-left"
tooltipPosition="bottom-start"
onClick={() => audio.toggle()} />
</Stack.Item>
<Stack.Item>
@@ -60,7 +60,7 @@ export const Panel = (props, context) => {
tooltip={settings.visible
? 'Close settings'
: 'Open settings'}
tooltipPosition="bottom-left"
tooltipPosition="bottom-start"
onClick={() => settings.toggle()} />
</Stack.Item>
</Stack>
+5 -5
View File
@@ -58,31 +58,31 @@ export const MESSAGE_TYPES = [
type: MESSAGE_TYPE_RADIO,
name: 'Radio',
description: 'All departments of radio messages',
selector: '.alert, .minorannounce, .syndradio, .airadio, .entradio, .centcomradio, .aiprivradio, .comradio, .secradio, .engradio, .medradio, .sciradio, .suppradio, .servradio, .radio, .deptradio, .binarysay, .newscaster',
selector: '.alert, .minorannounce, .syndradio, .centcomradio, .aiprivradio, .comradio, .secradio, .engradio, .medradio, .sciradio, .suppradio, .servradio, .radio, .deptradio, .binarysay, .newscaster, .resonate',
},
{
type: MESSAGE_TYPE_INFO,
name: 'Info',
description: 'Non-urgent messages from the game and items',
selector: '.notice:not(.pm), .adminnotice, .info, .sinister, .cult, .infoplain',
selector: '.notice:not(.pm), .adminnotice, .info, .sinister, .cult, .infoplain, .announce, .hear, .smallnotice, .holoparasite',
},
{
type: MESSAGE_TYPE_WARNING,
name: 'Warnings',
description: 'Urgent messages from the game and items',
selector: '.warning:not(.pm), .critical, .userdanger, .italics',
selector: '.warning:not(.pm), .critical, .userdanger, .italics, .alertsyndie, .warningplain',
},
{
type: MESSAGE_TYPE_DEADCHAT,
name: 'Deadchat',
description: 'All of deadchat',
selector: '.deadsay',
selector: '.deadsay, .ghostalert',
},
{
type: MESSAGE_TYPE_OOC,
name: 'OOC',
description: 'The bluewall of global OOC messages',
selector: '.ooc, .adminooc',
selector: '.ooc, .adminooc, .oocplain',
},
{
type: 'looc',
+3 -1
View File
@@ -117,7 +117,9 @@ export const chatMiddleware = store => {
const settings = selectSettings(store.getState());
chatRenderer.setHighlight(
settings.highlightText,
settings.highlightColor);
settings.highlightColor,
settings.matchWord,
settings.matchCase);
return;
}
if (type === 'roundrestart') {
+6 -8
View File
@@ -170,23 +170,19 @@ class ChatRenderer {
}
}
setHighlight(text, color) {
setHighlight(text, color, matchWord, matchCase) {
if (!text || !color) {
this.highlightRegex = null;
this.highlightColor = null;
return;
}
// citadel update - ig changed to i for flags,
// to fix issues with highlighting only working for every other word.
const allowedRegex = /^[a-z0-9_\-\s]+$/i;
const lines = String(text)
.split(',')
.map(str => str.trim())
// eslint-disable-next-line no-useless-escape
.map(str => str.trim().replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'))
.filter(str => (
// Must be longer than one character
str && str.length > 1
// Must be alphanumeric (with some punctuation)
&& allowedRegex.test(str)
));
// Nothing to match, reset highlighting
if (lines.length === 0) {
@@ -194,7 +190,9 @@ class ChatRenderer {
this.highlightColor = null;
return;
}
this.highlightRegex = new RegExp('(' + lines.join('|') + ')', 'gi');
const pattern = `${(matchWord ? '\\b' : '')}(${lines.join('|')})${(matchWord ? '\\b' : '')}`;
const flags = 'g' + (matchCase ? '' : 'i');
this.highlightRegex = new RegExp(pattern, flags);
this.highlightColor = color;
}
+8 -11
View File
@@ -10,7 +10,7 @@ import './styles/themes/light.scss';
import { perf } from 'common/perf';
import { combineReducers } from 'common/redux';
import { setupHotReloading } from 'tgui-dev-server/link/client';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
import { setupGlobalEvents } from 'tgui/events';
import { captureExternalLinks } from 'tgui/links';
import { createRenderer } from 'tgui/renderer';
@@ -93,8 +93,13 @@ const setupApp = () => {
'pos': '0x0',
'size': '0x0',
});
// Absolutely shit workaround for chatbox visiblity.
based_winset();
// Resize the panel to match the non-browser output
Byond.winget('output').then(output => {
Byond.winset('browseroutput', {
'size': output.size,
});
});
// Enable hot module reloading
if (module.hot) {
@@ -114,12 +119,4 @@ const setupApp = () => {
}
};
const based_winset = async (based_on_what = 'output') => {
// shitty workaround because winget is async.
const winget_output = await Byond.winget(based_on_what);
Byond.winset('browseroutput', {
'size': winget_output["size"],
});
};
setupApp();
+1 -1
View File
@@ -4,7 +4,7 @@
"version": "4.3.0",
"dependencies": {
"common": "workspace:*",
"dompurify": "^2.2.7",
"dompurify": "^2.3.1",
"inferno": "^7.4.8",
"tgui": "workspace:*",
"tgui-dev-server": "workspace:*",
@@ -56,6 +56,8 @@ export const SettingsGeneral = (props, context) => {
lineHeight,
highlightText,
highlightColor,
matchWord,
matchCase,
} = useSelector(context, selectSettings);
const dispatch = useDispatch(context);
const [freeFont, setFreeFont] = useLocalState(context, "freeFont", false);
@@ -134,7 +136,7 @@ export const SettingsGeneral = (props, context) => {
<Box>
<Flex mb={1} color="label" align="baseline">
<Flex.Item grow={1}>
Highlight words (comma separated):
Highlight text (comma separated):
</Flex.Item>
<Flex.Item shrink={0}>
<ColorBox mr={1} color={highlightColor} />
@@ -154,6 +156,22 @@ export const SettingsGeneral = (props, context) => {
onChange={(e, value) => dispatch(updateSettings({
highlightText: value,
}))} />
<Button.Checkbox
checked={matchWord}
tooltipPosition="bottom-start"
tooltip="Not compatible with punctuation."
onClick={() => dispatch(updateSettings({
matchWord: !matchWord,
}))}>
Match word
</Button.Checkbox>
<Button.Checkbox
checked={matchCase}
onClick={() => dispatch(updateSettings({
matchCase: !matchCase,
}))}>
Match case
</Button.Checkbox>
</Box>
<Divider />
<Box>
@@ -16,6 +16,8 @@ const initialState = {
adminMusicVolume: 0.5,
highlightText: '',
highlightColor: '#ffdd44',
matchWord: false,
matchCase: false,
view: {
visible: false,
activeTab: SETTINGS_TABS[0].id,
@@ -4,6 +4,7 @@
*/
@use 'sass:color';
@use 'sass:math';
@use '~tgui/styles/base.scss';
@use '~tgui/styles/colors.scss';
@@ -62,7 +63,7 @@ $color-bg-section: base.$color-bg-section !default;
content: '';
display: block;
margin-top: -0.75em;
border-bottom: (1em / 6) solid colors.$red;
border-bottom: math.div(1em, 6) solid colors.$red;
}
}
@@ -82,7 +83,7 @@ $color-bg-section: base.$color-bg-section !default;
.ChatMessage--highlighted {
position: relative;
border-left: (1em / 6) solid rgba(255, 221, 68);
border-left: math.div(1em, 6) solid rgba(255, 221, 68);
padding-left: 0.5em;
&:after {
@@ -3,12 +3,14 @@
* SPDX-License-Identifier: MIT
*/
@use 'sass:math';
$border-color: rgba(140, 140, 140, 0.5) !default;
.Ping {
position: relative;
padding: 0.125em 0.25em;
border: (1em / 12) solid $border-color;
border: math.div(1em, 12) solid $border-color;
border-radius: 0.25em;
width: 3.75em;
text-align: right;
@@ -287,16 +287,6 @@ em {
font-weight: bold;
}
.looc {
color: #6699CC;
font-weight: bold;
}
.antagooc {
color: #b8002e;
font-weight: bold;
}
.adminobserverooc {
color: #0099cc;
font-weight: bold;
@@ -338,7 +328,7 @@ em {
font-weight: bold;
}
.say, .emote, .infoplain {
.say, .emote, .infoplain, .oocplain, .warningplain {
}
.deadsay {
@@ -357,6 +347,9 @@ em {
color: #88ff88;
}
/* RADIO COLORS */
/* IF YOU CHANGE THIS KEEP IT IN SYNC WITH TGUI CONSTANTS */
.radio {
color: #1ecc43;
}
@@ -409,6 +402,14 @@ em {
color: #3434fd;
}
.greenteamradio {
color: #34fd34;
}
.yellowteamradio {
color: #fdfd34;
}
.yell {
font-weight: bold;
}
@@ -993,6 +994,10 @@ em {
width: auto;
}
.bigicon {
font-size: 2.5em;
}
.memo {
color: #638500;
text-align: center;
@@ -356,7 +356,7 @@ em {
font-weight: bold;
}
.say, .emote, .infoplain {
.say, .emote, .infoplain, .oocplain, .warningplain {
}
.deadsay {
@@ -430,6 +430,14 @@ em {
color: #0000ff;
}
.greenteamradio {
color: #00ff00;
}
.yellowteamradio {
color: #d1ba22;
}
.yell {
font-weight: bold;
}
@@ -1032,6 +1040,10 @@ h1.alert, h2.alert {
width: auto;
}
.bigicon {
font-size: 2.5em;
}
.memo {
color: #638500;
text-align: center;
+1 -5
View File
@@ -14,8 +14,4 @@ import './ie8';
import './dom4';
import './css-om';
import './inferno';
// Fetch is required for Webpack HMR
if (module.hot) {
require('whatwg-fetch');
}
import 'unfetch/polyfill';
+3 -3
View File
@@ -3,8 +3,8 @@
"name": "tgui-polyfill",
"version": "4.3.0",
"dependencies": {
"core-js": "^3.10.1",
"regenerator-runtime": "^0.13.8",
"whatwg-fetch": "^3.6.2"
"core-js": "^3.16.1",
"regenerator-runtime": "^0.13.9",
"unfetch": "^4.2.0"
}
}
@@ -63,7 +63,7 @@ export class AnimatedNumber extends Component {
if (!isSafeNumber(targetValue)) {
return targetValue || null;
}
let formattedValue = currentValue;
let formattedValue;
// Use custom formatter
if (format) {
formattedValue = format(currentValue);
+2
View File
@@ -38,6 +38,7 @@ export interface BoxProps {
bold?: BooleanLike;
italic?: BooleanLike;
nowrap?: BooleanLike;
preserveWhitespace?: BooleanLike;
m?: string | BooleanLike;
mx?: string | BooleanLike;
my?: string | BooleanLike;
@@ -163,6 +164,7 @@ const styleMapperByPropName = {
bold: mapBooleanPropTo('font-weight', 'bold'),
italic: mapBooleanPropTo('font-style', 'italic'),
nowrap: mapBooleanPropTo('white-space', 'nowrap'),
preserveWhitespace: mapBooleanPropTo('white-space', 'pre-wrap'),
// Margins
m: mapDirectionalUnitPropTo('margin', halfUnit, [
'top', 'bottom', 'left', 'right',
+25 -17
View File
@@ -28,7 +28,6 @@ export const Button = props => {
selected,
tooltip,
tooltipPosition,
tooltipOverrideLong,
ellipsis,
compact,
circular,
@@ -49,7 +48,7 @@ export const Button = props => {
}
// IE8: Use a lowercase "onclick" because synthetic events are fucked.
// IE8: Use an "unselectable" prop because "user-select" doesn't work.
return (
let buttonContent = (
<Box
className={classes([
'Button',
@@ -106,14 +105,18 @@ export const Button = props => {
rotation={iconRotation}
spin={iconSpin} />
)}
{tooltip && (
<Tooltip
content={tooltip}
overrideLong={tooltipOverrideLong}
position={tooltipPosition} />
)}
</Box>
);
if (tooltip) {
buttonContent = (
<Tooltip content={tooltip} position={tooltipPosition}>
{buttonContent}
</Tooltip>
);
}
return buttonContent;
};
Button.defaultHooks = pureComponentHooks;
@@ -234,14 +237,13 @@ export class ButtonInput extends Component {
iconSpin,
tooltip,
tooltipPosition,
tooltipOverrideLong,
color = 'default',
placeholder,
maxLength,
...rest
} = this.props;
return (
let buttonContent = (
<Box
className={classes([
'Button',
@@ -281,15 +283,21 @@ export class ButtonInput extends Component {
}
}}
/>
{tooltip && (
<Tooltip
content={tooltip}
overrideLong={tooltipOverrideLong}
position={tooltipPosition}
/>
)}
</Box>
);
if (tooltip) {
buttonContent = (
<Tooltip
content={tooltip}
position={tooltipPosition}
>
{buttonContent}
</Tooltip>
);
}
return buttonContent;
}
}
@@ -0,0 +1,208 @@
import { computeBoxProps } from "./Box";
import { Stack } from "./Stack";
import { ProgressBar } from "./ProgressBar";
import { Button } from "./Button";
import { Component } from 'inferno';
const ZOOM_MIN_VAL = 0.5;
const ZOOM_MAX_VAL = 1.5;
const ZOOM_INCREMENT = 0.1;
export class InfinitePlane extends Component {
constructor() {
super();
this.state = {
mouseDown: false,
left: 0,
top: 0,
lastLeft: 0,
lastTop: 0,
zoom: 1,
};
this.handleMouseDown = this.handleMouseDown.bind(this);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.handleZoomIncrease = this.handleZoomIncrease.bind(this);
this.handleZoomDecrease = this.handleZoomDecrease.bind(this);
this.onMouseUp = this.onMouseUp.bind(this);
this.doOffsetMouse = this.doOffsetMouse.bind(this);
}
componentDidMount() {
window.addEventListener("mouseup", this.onMouseUp);
window.addEventListener("mousedown", this.doOffsetMouse);
window.addEventListener("mousemove", this.doOffsetMouse);
window.addEventListener("mouseup", this.doOffsetMouse);
}
componentWillUnmount() {
window.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("mousedown", this.doOffsetMouse);
window.removeEventListener("mousemove", this.doOffsetMouse);
window.removeEventListener("mouseup", this.doOffsetMouse);
}
doOffsetMouse(event) {
const { zoom } = this.state;
event.screenZoomX = event.screenX * Math.pow(zoom, -1);
event.screenZoomY = event.screenY * Math.pow(zoom, -1);
}
handleMouseDown(event) {
this.setState((state) => {
return {
mouseDown: true,
lastLeft: event.clientX - state.left,
lastTop: event.clientY - state.top,
};
});
}
onMouseUp() {
this.setState({
mouseDown: false,
});
}
handleZoomIncrease(event) {
const { onZoomChange } = this.props;
const { zoom } = this.state;
const newZoomValue = Math.min(zoom+ZOOM_INCREMENT, ZOOM_MAX_VAL);
this.setState({
zoom: newZoomValue,
});
if (onZoomChange) {
onZoomChange(newZoomValue);
}
}
handleZoomDecrease(event) {
const { onZoomChange } = this.props;
const { zoom } = this.state;
const newZoomValue = Math.max(zoom-ZOOM_INCREMENT, ZOOM_MIN_VAL);
this.setState({
zoom: newZoomValue,
});
if (onZoomChange) {
onZoomChange(newZoomValue);
}
}
handleMouseMove(event) {
const {
onBackgroundMoved,
initialLeft = 0,
initialTop = 0,
} = this.props;
if (this.state.mouseDown) {
let newX, newY;
this.setState((state) => {
newX = event.clientX - state.lastLeft;
newY = event.clientY - state.lastTop;
return {
left: newX,
top: newY,
};
});
if (onBackgroundMoved) {
onBackgroundMoved(newX+initialLeft, newY+initialTop);
}
}
}
render() {
const {
children,
backgroundImage,
imageWidth,
initialLeft = 0,
initialTop = 0,
...rest
} = this.props;
const {
left,
top,
zoom,
} = this.state;
const finalLeft = initialLeft + left;
const finalTop = initialTop + top;
return (
<div
ref={this.ref}
{...computeBoxProps({
...rest,
style: {
...rest.style,
overflow: "hidden",
position: "relative",
},
})}
>
<div
onMouseDown={this.handleMouseDown}
onMouseMove={this.handleMouseMove}
style={{
"position": "fixed",
"height": "100%",
"width": "100%",
"background-image": `url("${backgroundImage}")`,
"background-position": `${finalLeft}px ${finalTop}px`,
"background-repeat": "repeat",
"background-size": `${zoom*imageWidth}px`,
}}
/>
<div
onMouseDown={this.handleMouseDown}
onMouseMove={this.handleMouseMove}
style={{
"position": "fixed",
"transform": `translate(${finalLeft}px, ${finalTop}px) scale(${zoom})`,
"transform-origin": "top left",
"height": "100%",
"width": "100%",
}}
>
{children}
</div>
<Stack
position="absolute"
width="100%"
>
<Stack.Item>
<Button
icon="minus"
onClick={this.handleZoomDecrease}
/>
</Stack.Item>
<Stack.Item grow={1}>
<ProgressBar
minValue={ZOOM_MIN_VAL}
value={zoom}
maxValue={ZOOM_MAX_VAL}
>
{zoom}x
</ProgressBar>
</Stack.Item>
<Stack.Item>
<Button
icon="plus"
onClick={this.handleZoomIncrease}
/>
</Stack.Item>
</Stack>
</div>
);
}
}
+72
View File
@@ -0,0 +1,72 @@
import { createPopper, OptionsGeneric } from "@popperjs/core";
import { Component, findDOMfromVNode, InfernoNode, render } from "inferno";
type PopperProps = {
popperContent: InfernoNode;
options?: Partial<OptionsGeneric<unknown>>;
additionalStyles?: CSSProperties,
};
export class Popper extends Component<PopperProps> {
static id: number = 0;
renderedContent: HTMLDivElement;
popperInstance: ReturnType<typeof createPopper>;
constructor() {
super();
Popper.id += 1;
}
componentDidMount() {
const {
additionalStyles,
options,
} = this.props;
this.renderedContent = document.createElement("div");
if (additionalStyles) {
for (const [attribute, value] of Object.entries(additionalStyles)) {
this.renderedContent.style[attribute] = value;
}
}
this.renderPopperContent(() => {
document.body.appendChild(this.renderedContent);
this.popperInstance = createPopper(
// HACK: We don't want to create a wrapper, as it could break the layout
// of consumers, so we do the inferno equivalent of `findDOMNode(this)`.
// This is usually bad as refs are usually better, but refs did
// not work in this case, as they weren't propagating correctly.
// A previous attempt was made as a render prop that passed an ID,
// but this made consuming use too unwieldly.
// This code is copied from `findDOMNode` in inferno-extras.
// Because this component is written in TypeScript, we will know
// immediately if this internal variable is removed.
findDOMfromVNode(this.$LI, true),
this.renderedContent,
options,
);
});
}
componentDidUpdate() {
this.renderPopperContent(() => this.popperInstance?.update());
}
componentWillUnmount() {
this.popperInstance?.destroy();
this.renderedContent.remove();
this.renderedContent = null;
}
renderPopperContent(callback: () => void) {
render(this.props.popperContent, this.renderedContent, callback);
}
render() {
return this.props.children;
}
}
+10 -9
View File
@@ -35,15 +35,16 @@ export const RoundGauge = props => {
minValue,
maxValue);
const clampedValue = clamp01(scaledValue);
let scaledRanges = ranges ? {} : { "primary": [0, 1] };
if (ranges)
{ Object.keys(ranges).forEach(x => {
const range = ranges[x];
scaledRanges[x] = [
scale(range[0], minValue, maxValue),
scale(range[1], minValue, maxValue),
];
}); }
const scaledRanges = ranges ? {} : { "primary": [0, 1] };
if (ranges) {
Object.keys(ranges).forEach(x => {
const range = ranges[x];
scaledRanges[x] = [
scale(range[0], minValue, maxValue),
scale(range[1], minValue, maxValue),
];
});
}
let alertColor = null;
if (alertAfter < value) {
-28
View File
@@ -1,28 +0,0 @@
/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
*/
import { classes } from 'common/react';
export const Tooltip = props => {
const {
content,
overrideLong = false,
position = 'bottom',
} = props;
// Empirically calculated length of the string,
// at which tooltip text starts to overflow.
const long = typeof content === 'string'
&& (content.length > 35 && !overrideLong);
return (
<div
className={classes([
'Tooltip',
long && 'Tooltip--long',
position && 'Tooltip--' + position,
])}
data-tooltip={content} />
);
};
+73
View File
@@ -0,0 +1,73 @@
import { Placement } from '@popperjs/core';
import { Component, findDOMfromVNode, InfernoNode } from 'inferno';
import { Popper } from "./Popper";
const DEFAULT_PLACEMENT = "top";
type TooltipProps = {
children?: InfernoNode;
content: string;
position?: Placement,
};
type TooltipState = {
hovered: boolean;
};
export class Tooltip extends Component<TooltipProps, TooltipState> {
constructor() {
super();
this.state = {
hovered: false,
};
}
componentDidMount() {
// HACK: We don't want to create a wrapper, as it could break the layout
// of consumers, so we do the inferno equivalent of `findDOMNode(this)`.
// My attempt to avoid this was a render prop that passed in
// callbacks to onmouseenter and onmouseleave, but this was unwiedly
// to consumers, specifically buttons.
// This code is copied from `findDOMNode` in inferno-extras.
// Because this component is written in TypeScript, we will know
// immediately if this internal variable is removed.
const domNode = findDOMfromVNode(this.$LI, true);
domNode.addEventListener("mouseenter", () => {
this.setState({
hovered: true,
});
});
domNode.addEventListener("mouseleave", () => {
this.setState({
hovered: false,
});
});
}
render() {
return (
<Popper
options={{
placement: this.props.position || "auto",
}}
popperContent={
<div
className="Tooltip"
style={{
opacity: this.state.hovered ? 1 : 0,
}}>
{this.props.content}
</div>
}
additionalStyles={{
"pointer-events": "none",
}}>
{this.props.children}
</Popper>
);
}
}
+2
View File
@@ -20,6 +20,7 @@ export { Dropdown } from './Dropdown';
export { Flex } from './Flex';
export { Grid } from './Grid';
export { Icon } from './Icon';
export { InfinitePlane } from './InfinitePlane';
export { Input } from './Input';
export { Knob } from './Knob';
export { LabeledControls } from './LabeledControls';
@@ -28,6 +29,7 @@ export { Modal } from './Modal';
export { NoticeBox } from './NoticeBox';
export { NumberInput } from './NumberInput';
export { ProgressBar } from './ProgressBar';
export { Popper } from './Popper';
export { RoundGauge } from './RoundGauge';
export { Section } from './Section';
export { Slider } from './Slider';
+13 -2
View File
@@ -59,11 +59,12 @@ export const CSS_COLORS = [
'label',
];
/* IF YOU CHANGE THIS KEEP IT IN SYNC WITH CHAT CSS */
export const RADIO_CHANNELS = [
{
name: 'Syndicate',
freq: 1213,
color: '#a52a2a',
color: '#8f4a4b',
},
{
name: 'Red Team',
@@ -75,6 +76,16 @@ export const RADIO_CHANNELS = [
freq: 1217,
color: '#3434fd',
},
{
name: 'Green Team',
freq: 1219,
color: '#34fd34',
},
{
name: 'Yellow Team',
freq: 1221,
color: '#fdfd34',
},
{
name: 'CentCom',
freq: 1337,
@@ -98,7 +109,7 @@ export const RADIO_CHANNELS = [
{
name: 'Command',
freq: 1353,
color: '#5177ff',
color: '#fcdf03',
},
{
name: 'Medical',
+1 -1
View File
@@ -39,7 +39,7 @@ export const debugMiddleware = store => {
};
export const relayMiddleware = store => {
const devServer = require('tgui-dev-server/link/client');
const devServer = require('tgui-dev-server/link/client.cjs');
const externalBrowser = location.search === '?external';
if (externalBrowser) {
devServer.subscribe(msg => {
+1 -1
View File
@@ -105,7 +105,7 @@ export const formatMoney = (value, precision = 0) => {
*/
export const formatDb = value => {
const db = 20 * Math.log(value) / Math.log(10);
const sign = db >= 0 ? '+' : db < 0 ? '' : '';
const sign = db >= 0 ? '+' : '';
let formatted = Math.abs(db);
if (formatted === Infinity) {
formatted = 'Inf';
+1 -1
View File
@@ -19,7 +19,7 @@ import './styles/themes/wizard.scss';
import './styles/themes/clockcult.scss';
import { perf } from 'common/perf';
import { setupHotReloading } from 'tgui-dev-server/link/client';
import { setupHotReloading } from 'tgui-dev-server/link/client.cjs';
import { setupHotKeys } from './hotkeys';
import { captureExternalLinks } from './links';
import { createRenderer } from './renderer';
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend, useSharedState } from '../backend';
import { Button, LabeledList, NoticeBox, Section, Tabs } from '../components';
import { Window } from '../layouts';
@@ -10,8 +9,7 @@ export const AbductorConsole = (props, context) => {
<Window
theme="abductor"
width={600}
height={532}
resizable>
height={532}>
<Window.Content scrollable>
<Tabs>
<Tabs.Tab
@@ -33,10 +31,10 @@ export const AbductorConsole = (props, context) => {
<Abductsoft />
)}
{tab === 2 && (
<Fragment>
<>
<EmergencyTeleporter />
<VestSettings />
</Fragment>
</>
)}
</Window.Content>
</Window>
@@ -60,7 +58,7 @@ const Abductsoft = (props, context) => {
}
return (
<Fragment>
<>
<Section>
<LabeledList>
<LabeledList.Item label="Collected Samples">
@@ -71,7 +69,7 @@ const Abductsoft = (props, context) => {
<GenericUplink
currencyAmount={credits}
currencySymbol="Credits" />
</Fragment>
</>
);
};
@@ -0,0 +1,126 @@
import { useBackend, useLocalState } from '../backend';
import { Button, LabeledList, Section, Box, NoticeBox, Table } from '../components';
import { Window } from '../layouts';
import { AdventureDataProvider, AdventureScreen } from './ExodroneConsole';
import { formatTime } from '../format';
type Adventure = {
ref: string;
name: string;
id: string;
approved: boolean;
uploader: string;
version: number;
timestamp: string;
json_status: string;
};
type AdventureBrowserData = AdventureDataProvider & {
adventures: Array<Adventure>;
feedback_message: string;
play_mode: boolean;
adventure_data: any;
delay_time: number;
delay_message: string;
};
const AdventureEntry = (props, context) => {
const { data, act } = useBackend<AdventureBrowserData>(context);
const { entry_ref, close }: { entry_ref: string, close: () => void } = props;
const entry = data.adventures.find(x => x.ref === entry_ref);
return (
<Section>
<LabeledList>
<LabeledList.Item label="ID">{entry.id}</LabeledList.Item>
<LabeledList.Item label="Name">{entry.name}</LabeledList.Item>
<LabeledList.Item label="JSON Version">{entry.version}</LabeledList.Item>
<LabeledList.Item label="Uploader">{entry.uploader}</LabeledList.Item>
<LabeledList.Item label="Last Update">{entry.timestamp}</LabeledList.Item>
<LabeledList.Item label="Approved">
<Button.Checkbox
checked={entry.approved}
onClick={() => act("approve", { ref: entry.ref })}
/>
</LabeledList.Item>
<LabeledList.Item label="JSON">
{entry.json_status}
<Button onClick={() => act("upload", { ref: entry.ref })} content="Upload" />
<Button onClick={() => act("download", { ref: entry.ref })} content="Download" />
</LabeledList.Item>
</LabeledList>
<Button.Confirm onClick={() => { close(); act("delete", { ref: entry.ref }); }} content="Delete" />
<Button onClick={() => act("play", { ref: entry.ref })} content="Play" />
<Button onClick={() => act("refresh", { ref: entry.ref })} content="Refresh" />
<Button.Confirm onClick={() => act("save", { ref: entry.ref })} content="Save" />
<Button onClick={close} content="Close" />
</Section>
);
};
const AdventureList = (props, context) => {
const { data, act } = useBackend<AdventureBrowserData>(context);
const [
openAdventure,
setOpenAdventure,
] = useLocalState(context, 'openAdventure', null);
return (
<>
{openAdventure && (
<AdventureEntry
entry_ref={openAdventure}
close={() => setOpenAdventure(null)} />
)}
{!openAdventure && (
<Table>
<Table.Row>
<Table.Cell color="label">ID</Table.Cell>
<Table.Cell color="label">Title</Table.Cell>
<Table.Cell color="label">Edit</Table.Cell>
</Table.Row>
{data.adventures.map(p => (
<Table.Row
key={p.ref}
className="candystripe">
<Table.Cell>{p.id}</Table.Cell>
<Table.Cell>{p.name}</Table.Cell>
<Table.Cell><Button icon="edit" onClick={() => setOpenAdventure(p.ref)} /></Table.Cell>
</Table.Row>
))}
<Table.Row>
<Button onClick={() => act("create")}>Create New</Button>
</Table.Row>
</Table>
)}
</>
);
};
const DebugPlayer = (props, context) => {
const { data, act } = useBackend<AdventureBrowserData>(context);
return (
<Section
title="Playtest"
buttons={<Button onClick={() => act("end_play")}>End Playtest</Button>}>
{data.delay_time > 0
? <Box>DELAY {formatTime(data.delay_time)} / {data.delay_message}</Box>
: <AdventureScreen hide_status />}
</Section>);
};
export const AdventureBrowser = (props, context) => {
const { data } = useBackend<AdventureBrowserData>(context);
return (
<Window width={650} height={500} title="Adventure Manager">
<Window.Content>
{!!data.feedback_message && (
<NoticeBox>
{data.feedback_message}
</NoticeBox>
)}
{data.play_mode ? (<DebugPlayer />) : (<AdventureList />)}
</Window.Content>
</Window>
);
};
+2 -3
View File
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
@@ -68,7 +67,7 @@ export const AiAirlock = (props, context) => {
label="Electrify"
color={statusElectrify.color}
buttons={(
<Fragment>
<>
<Button
icon="wrench"
disabled={!(data.wires.shock && data.shock === 0)}
@@ -84,7 +83,7 @@ export const AiAirlock = (props, context) => {
disabled={!data.wires.shock}
content="Permanent"
onClick={() => act('shock-perm')} />
</Fragment>
</>
)}>
{data.shock === 2 ? 'Safe' : 'Electrified'}
{' '}
+3 -5
View File
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
@@ -7,8 +6,7 @@ export const AiRestorer = () => {
return (
<Window
width={370}
height={360}
resizable>
height={360}>
<Window.Content scrollable>
<AiRestorerContent />
</Window.Content>
@@ -29,7 +27,7 @@ export const AiRestorerContent = (props, context) => {
ejectable,
} = data;
return (
<Fragment>
<>
{error && (
<NoticeBox textAlign="center">
{error}
@@ -93,6 +91,6 @@ export const AiRestorerContent = (props, context) => {
</Section>
</Section>
)}
</Fragment>
</>
);
};
+5 -6
View File
@@ -12,8 +12,7 @@ export const AirAlarm = (props, context) => {
return (
<Window
width={440}
height={650}
resizable>
height={650}>
<Window.Content scrollable>
<InterfaceLockNoticeBox />
<AirAlarmStatus />
@@ -48,7 +47,7 @@ const AirAlarmStatus = (props, context) => {
<Section title="Air Status">
<LabeledList>
{entries.length > 0 && (
<Fragment>
<>
{entries.map(entry => {
const status = dangerMap[entry.danger_level] || dangerMap[0];
return (
@@ -72,7 +71,7 @@ const AirAlarmStatus = (props, context) => {
|| data.fire_alarm && 'Fire Alarm'
|| 'Nominal'}
</LabeledList.Item>
</Fragment>
</>
) || (
<LabeledList.Item
label="Warning"
@@ -145,7 +144,7 @@ const AirAlarmControlHome = (props, context) => {
atmos_alarm,
} = data;
return (
<Fragment>
<>
<Button
icon={atmos_alarm
? 'exclamation-triangle'
@@ -183,7 +182,7 @@ const AirAlarmControlHome = (props, context) => {
icon="chart-bar"
content="Alarm Thresholds"
onClick={() => setScreen('thresholds')} />
</Fragment>
</>
);
};
+126 -51
View File
@@ -6,68 +6,143 @@
import { clamp01 } from 'common/math';
import { useBackend } from '../backend';
import { Box, Button, Flex } from '../components';
import { Component, createRef } from 'inferno';
import { Box, Flex, Section } from '../components';
import { Window } from '../layouts';
import {
KEY_ENTER,
KEY_LEFT,
KEY_RIGHT,
KEY_SPACE,
KEY_TAB,
} from 'common/keycodes';
export const AlertModal = (props, context) => {
const { act, data } = useBackend(context);
const {
title,
message,
buttons,
timeout,
} = data;
export class AlertModal extends Component {
constructor() {
super();
return (
<Window
title={title}
width={350}
height={150}
resizable>
{timeout !== undefined && <Loader value={timeout} />}
<Window.Content>
<Flex direction="column" height="100%">
<Flex.Item grow={1}>
<Flex
direction="column"
className="AlertModal__Message"
height="100%">
<Flex.Item>
<Box m={1}>
{message}
</Box>
this.buttonRefs = [createRef()];
this.state = { current: 0 };
}
componentDidMount() {
const { data } = useBackend(this.context);
const { buttons, autofocus } = data;
const { current } = this.state;
const button = this.buttonRefs[current].current;
// Fill ref array with refs for other buttons
for (let i = 1; i < buttons.length; i++) {
this.buttonRefs.push(createRef());
}
if (autofocus) {
setTimeout(() => button.focus(), 1);
}
}
setCurrent(current, isArrowKey) {
const { data } = useBackend(this.context);
const { buttons } = data;
// Mimic alert() behavior for tabs and arrow keys
if (current >= buttons.length) {
current = isArrowKey ? current - 1 : 0;
} else if (current < 0) {
current = isArrowKey ? 0 : buttons.length - 1;
}
const button = this.buttonRefs[current].current;
// Prevents an error from occurring on close
if (button) {
setTimeout(() => button.focus(), 1);
}
this.setState({ current });
}
render() {
const { act, data } = useBackend(this.context);
const { title, message, buttons, timeout } = data;
const { current } = this.state;
const focusCurrentButton = () => this.setCurrent(current, false);
return (
<Window
title={title}
width={350}
height={150}
canClose={timeout > 0}>
{timeout && <Loader value={timeout} />}
<Window.Content
onFocus={focusCurrentButton}
onClick={focusCurrentButton}>
<Section fill>
<Flex direction="column" height="100%">
<Flex.Item grow={1}>
<Flex
direction="column"
className="AlertModal__Message"
height="100%">
<Flex.Item>
<Box m={1}>
{message}
</Box>
</Flex.Item>
</Flex>
</Flex.Item>
<Flex.Item my={2}>
<Flex className="AlertModal__Buttons">
{buttons.map((button, buttonIndex) => (
<Flex.Item key={buttonIndex} mx={1}>
<div
ref={this.buttonRefs[buttonIndex]}
className="Button Button--color--default"
px={3}
onClick={() => act("choose", { choice: button })}
onKeyDown={e => {
const keyCode = window.event ? e.which : e.keyCode;
/**
* Simulate a click when pressing space or enter,
* allow keyboard navigation, override tab behavior
*/
if (keyCode === KEY_SPACE || keyCode === KEY_ENTER) {
act("choose", { choice: button });
} else if (
keyCode === KEY_LEFT
|| (e.shiftKey && keyCode === KEY_TAB)
) {
this.setCurrent(current - 1, keyCode === KEY_LEFT);
} else if (
keyCode === KEY_RIGHT || keyCode === KEY_TAB
) {
this.setCurrent(current + 1, keyCode === KEY_RIGHT);
}
}}>
{button}
</div>
</Flex.Item>
))}
</Flex>
</Flex.Item>
</Flex>
</Flex.Item>
<Flex.Item my={2}>
<Flex className="AlertModal__Buttons">
{buttons.map(button => (
<Flex.Item key={button} mx={1}>
<Button
px={3}
onClick={() => act("choose", { choice: button })}>
{button}
</Button>
</Flex.Item>
))}
</Flex>
</Flex.Item>
</Flex>
</Window.Content>
</Window>
);
};
</Section>
</Window.Content>
</Window>
);
}
}
export const Loader = props => {
const { value } = props;
return (
<div
className="AlertModal__Loader">
<div className="AlertModal__Loader">
<Box
className="AlertModal__LoaderProgress"
style={{
width: clamp01(value) * 100 + '%',
}} />
style={{ width: clamp01(value) * 100 + '%' }} />
</div>
);
};
@@ -0,0 +1,86 @@
import { useBackend, useLocalState } from '../backend';
import { Blink, BlockQuote, Box, Dimmer, Icon, Section, Stack } from '../components';
import { BooleanLike } from 'common/react';
import { Window } from '../layouts';
type Objective = {
count: number;
name: string;
explanation: string;
complete: BooleanLike;
was_uncompleted: BooleanLike;
reward: number;
}
type Info = {
objectives: Objective[];
};
export const AntagInfoBrainwashed = (props, context) => {
return (
<Window
width={400}
height={400}
theme="abductor">
<Window.Content
backgroundColor="#722e7d">
<Icon
size={16}
name="flushed"
color="#3f1945"
position="absolute"
top="42%"
left="26%" />
<Section fill>
<Stack align="baseline" vertical fill>
<Stack.Item fontFamily="Wingdings">
Hey, no! Stop translating this!
</Stack.Item>
<Stack.Item mt={-0.25} fontSize="20px">
Your mind reels...
</Stack.Item>
<Stack.Item mt={-0.25} fontSize="20px">
It is focusing on a single purpose...
</Stack.Item>
<Stack.Item mt={1.5} grow >
<ObjectivePrintout />
</Stack.Item>
<Stack.Item fontSize="20px">
Follow the Directives, at any cost!
</Stack.Item>
<Stack.Item fontFamily="Wingdings">
You ruined my cool font effect.
</Stack.Item>
</Stack>
</Section>
</Window.Content>
</Window>
);
};
const ObjectivePrintout = (props, context) => {
const { data } = useBackend<Info>(context);
const {
objectives,
} = data;
return (
<Stack vertical>
<Stack.Item bold>
Your current objectives:
</Stack.Item>
<Stack.Item>
{!objectives && "None!"
|| objectives.map(objective => (
<>
<Stack.Item key={objective.count}>
#{objective.count}: {objective.explanation}
</Stack.Item>
<Stack.Item textColor="red">
This Directive must be followed.
</Stack.Item>
</>
)) }
</Stack.Item>
</Stack>
);
};
@@ -0,0 +1,54 @@
import { BlockQuote, Stack } from '../components';
import { Window } from '../layouts';
const goodstyle = {
color: 'lightgreen',
};
const badstyle = {
color: 'red',
};
const noticestyle = {
color: 'lightblue',
};
export const AntagInfoMorph = (props, context) => {
return (
<Window
width={620}
height={170}
theme="abductor">
<Window.Content>
<Stack vertical fill>
<Stack.Item fontSize="25px">
You are a morph...
</Stack.Item>
<Stack.Item>
<BlockQuote>
...a shapeshifting abomination that can eat almost anything.
You may take the form of anything you can
see by <span style={noticestyle}>shift-clicking</span> it.
<span style={badstyle}>
&ensp;This process will alert any nearby observers.
</span> While morphed,
you move faster, but are unable to
attack creatures or eat anything.
In addition,
<span style={badstyle}>
&ensp;anyone within three tiles will note an uncanny
wrongness if examining you.
</span> You can attack any item or dead
creature to consume it -
<span style={goodstyle}>
&ensp;creatures will restore your health.
</span> Finally, you can restore yourself
to your original form while morphed
by <span style={noticestyle}>shift-clicking</span> yourself.
</BlockQuote>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
@@ -0,0 +1,88 @@
import { BlockQuote, LabeledList, Section, Stack } from '../components';
import { Window } from '../layouts';
const tipstyle = {
color: 'white',
};
const noticestyle = {
color: 'lightblue',
};
export const AntagInfoNightmare = (props, context) => {
return (
<Window
width={620}
height={340}>
<Window.Content backgroundColor="#0d0d0d">
<Stack fill>
<Stack.Item width="46.2%">
<Section fill>
<Stack vertical fill>
<Stack.Item fontSize="25px">
You are a Nightmare.
</Stack.Item>
<Stack.Item>
<BlockQuote>
You are a creature from beyond the stars that
has incredibly strong powers in the darkness,
becoming nigh unbeatable. Unfortunately, you
wither and burn away in the light. You must use your
<span style={noticestyle}>&ensp;light eater</span> to
dim the station, making hunting easier.
</BlockQuote>
</Stack.Item>
<Stack.Divider />
<Stack.Item textColor="label">
<span style={tipstyle}>
Tip #1:&ensp;
</span>
Move often. The station will be hunting you
after you are discovered, so don&apos;t
stay in one area for long.<br />
<span style={tipstyle}>
Tip #2:&ensp;
</span>
Pick unfair fights. You are incredibly strong
in one versus one situations, use it. The more
you fight, the harder it will be to keep it dark.<br />
<span style={tipstyle}>
Tip #3:&ensp;
</span>
Fully destroy APCs when possible. Instead of
hunting lights that can be fixed, hunt the APCs
which are harder to repair.
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
<Stack.Item width="53%">
<Section fill title="Powers">
<LabeledList>
<LabeledList.Item label="Shadow Dance">
Whilst in the shadows, you are immune to all melee
and ranged attacks, whilst also rapidly regenerating
health.
</LabeledList.Item>
<LabeledList.Item label="Shadow Walk">
You are allowed unlimited, unrestricted
movement in the dark. Light will pull you out of this.
</LabeledList.Item>
<LabeledList.Item label="Heart of Darkness">
Your heart invites the shadows. If you
die in the darkness, you will eventually
revive if left alone.
</LabeledList.Item>
<LabeledList.Item label="Light Eater">
Your twisted appendage. It will consume
the light of what it touches,
be it victim or object.
</LabeledList.Item>
</LabeledList>
</Section>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
@@ -0,0 +1,246 @@
import { useBackend, useLocalState } from '../backend';
import { multiline } from 'common/string';
import { BlockQuote, Button, Dimmer, Section, Stack } from '../components';
import { BooleanLike } from 'common/react';
import { Window } from '../layouts';
const allystyle = {
fontWeight: 'bold',
color: 'yellow',
};
const badstyle = {
color: 'red',
fontWeight: 'bold',
};
const goalstyle = {
color: 'lightblue',
fontWeight: 'bold',
};
type Objective = {
count: number;
name: string;
explanation: string;
}
type Info = {
phrases: string;
responses: string;
theme: string;
allies: string;
goal: string;
intro: string;
code: string;
has_uplink: BooleanLike;
uplink_intro: string;
uplink_unlock_info: string;
objectives: Objective[];
};
const ObjectivePrintout = (props, context) => {
const { data } = useBackend<Info>(context);
const {
objectives,
} = data;
return (
<Stack vertical>
<Stack.Item bold>
Your current objectives:
</Stack.Item>
<Stack.Item>
{!objectives && "None!"
|| objectives.map(objective => (
<Stack.Item key={objective.count}>
#{objective.count}: {objective.explanation}
</Stack.Item>
)) }
</Stack.Item>
</Stack>
);
};
const IntroductionSection = (props, context) => {
const { act, data } = useBackend<Info>(context);
const {
intro,
} = data;
return (
<Section fill title="Intro" scrollable>
<Stack vertical fill>
<Stack.Item fontSize="25px">
{intro}
</Stack.Item>
<Stack.Item grow>
<ObjectivePrintout />
</Stack.Item>
</Stack>
</Section>
);
};
const EmployerSection = (props, context) => {
const { data } = useBackend<Info>(context);
const {
allies,
goal,
} = data;
return (
<Section
fill
title="Employer"
scrollable
buttons={
<Button
icon="hammer"
tooltip={multiline`
This is a gameplay suggestion for bored traitors.
You don't have to follow it... kinda like spacelaw!`}
tooltipPosition="bottom-start">
Policy
</Button>
}>
<Stack vertical fill>
<Stack.Item grow>
<Stack vertical>
<Stack.Item>
<span style={allystyle}>
Your allegiances:<br />
</span>
<BlockQuote>
{allies}
</BlockQuote>
</Stack.Item>
<Stack.Divider />
<Stack.Item>
<span style={goalstyle}>
Employer thoughts:<br />
</span>
<BlockQuote>
{goal}
</BlockQuote>
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Section>
);
};
const UplinkSection = (props, context) => {
const { data } = useBackend<Info>(context);
const {
has_uplink,
uplink_intro,
uplink_unlock_info,
code,
} = data;
return (
<Section
title="Uplink"
mb={!has_uplink && -1}>
<Stack fill>
{!has_uplink && (
<Dimmer>
<Stack.Item fontSize="18px">
You were not supplied with an uplink.
</Stack.Item>
</Dimmer>
) || (
<>
<Stack.Item bold>
{uplink_intro}
<br />
<span style={goalstyle}>Code: {code}</span>
</Stack.Item>
<Stack.Divider />
<Stack.Item mt="1%">
<BlockQuote>
{uplink_unlock_info}
</BlockQuote>
</Stack.Item>
</>
)}
</Stack>
</Section>
);
};
const CodewordsSection = (props, context) => {
const { data } = useBackend<Info>(context);
const {
phrases,
responses,
} = data;
return (
<Section title="Codewords">
<Stack fill>
<Stack.Item grow basis={0}>
<BlockQuote>
The Syndicate have provided you with the following
codewords to identify fellow agents. Use the codewords
during regular conversation to identify other agents.
Proceed with caution, however, as everyone is a
potential foe.
<span style={badstyle}>
&ensp;You have memorized the codewords, allowing you
to recognise them when heard.
</span>
</BlockQuote>
</Stack.Item>
<Stack.Divider mr={1} />
<Stack.Item grow basis={0}>
<Stack vertical>
<Stack.Item>
Code Phrases:
</Stack.Item>
<Stack.Item bold textColor="blue">
{phrases}
</Stack.Item>
<Stack.Item>
Code Responses:
</Stack.Item>
<Stack.Item bold textColor="red">
{responses}
</Stack.Item>
</Stack>
</Stack.Item>
</Stack>
</Section>
);
};
export const AntagInfoTraitor = (props, context) => {
const { data } = useBackend<Info>(context);
const {
theme,
} = data;
return (
<Window
width={620}
height={580}
theme={theme}>
<Window.Content>
<Stack vertical fill>
<Stack.Item grow>
<Stack fill>
<Stack.Item width="70%">
<IntroductionSection />
</Stack.Item>
<Stack.Item width="30%">
<EmployerSection />
</Stack.Item>
</Stack>
</Stack.Item>
<Stack.Item>
<UplinkSection />
</Stack.Item>
<Stack.Item>
<CodewordsSection />
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
@@ -0,0 +1,152 @@
import { useBackend, useLocalState } from '../backend';
import { Blink, BlockQuote, Box, Dimmer, Icon, Section, Stack } from '../components';
import { BooleanLike } from 'common/react';
import { Window } from '../layouts';
const teleportstyle = {
color: 'yellow',
};
const robestyle = {
color: 'lightblue',
};
const destructionstyle = {
color: 'red',
};
const defensestyle = {
color: 'orange',
};
const transportstyle = {
color: 'yellow',
};
const summonstyle = {
color: 'cyan',
};
const ritualstyle = {
color: 'violet',
};
type Objective = {
count: number;
name: string;
explanation: string;
complete: BooleanLike;
was_uncompleted: BooleanLike;
reward: number;
}
type Info = {
objectives: Objective[];
};
export const AntagInfoWizard = (props, context) => {
return (
<Window
width={620}
height={580}
theme="wizard">
<Window.Content>
<Stack vertical fill>
<Stack.Item grow>
<Section scrollable fill>
<Stack vertical>
<Stack.Item textColor="red" fontSize="20px">
You are the Space Wizard!
</Stack.Item>
<Stack.Item>
<ObjectivePrintout />
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
<Stack.Item>
<Section fill title="Spellbook">
<Stack vertical fill>
<Stack.Item grow>
You have a spellbook which is bound to you. You can use it
to choose a magical arsenal.<br />
<span style={destructionstyle}>
The deadly page has the offensive spells, to destroy your
enemies.
</span><br />
<span style={defensestyle}>
The defensive page has defensive spells, to keep yourself
alive. Remember, you may be powerful, but you are still
only human.
</span><br />
<span style={transportstyle}>
The transport page has mobility spells, very important
aspect of staying alive and getting things done.
</span><br />
<span style={summonstyle}>
The summoning page has summoning and other helpful spells
for not fighting alone. Careful, not every summon is on your
side.
</span><br />
<span style={ritualstyle}>
The rituals page has powerful global effects, that will pit
the station against itself. Do mind that these are either
expensive, or just for panache.
</span>
</Stack.Item>
<Stack.Item textColor="lightgreen">
(If you are unsure what to get or are new to the Federation,
go to the &quot;Wizard Approved Loadouts&quot; section.
There you will find some kits that work fairly well for new
wizards.)
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
<Stack.Item>
<Section title="Misc Gear">
<Stack>
<Stack.Item>
<span style={teleportstyle}>Teleport scroll:</span> 4
uses to teleport wherever you want.
You will not be able to come back to the den, so
be sure you have everything ready before departing.<br />
<span style={robestyle}>Wizard robes:</span> Used
to cast most spells. Your spellbook will let
you know which spells cannot be cast without a garb.
</Stack.Item>
</Stack>
</Section>
</Stack.Item>
<Stack.Item>
<Section textAlign="center" textColor="red" fontSize="20px">
Remember: Do not forget to prepare your spells.
</Section>
</Stack.Item>
</Stack>
</Window.Content>
</Window>
);
};
const ObjectivePrintout = (props, context) => {
const { data } = useBackend<Info>(context);
const {
objectives,
} = data;
return (
<Stack vertical>
<Stack.Item bold>
The Space Wizards Federation has given you the following tasks:
</Stack.Item>
<Stack.Item>
{!objectives && "None!"
|| objectives.map(objective => (
<Stack.Item key={objective.count}>
#{objective.count}: {objective.explanation}
</Stack.Item>
)) }
</Stack.Item>
</Stack>
);
};
+8 -10
View File
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components';
import { Window } from '../layouts';
@@ -8,8 +7,7 @@ export const Apc = (props, context) => {
return (
<Window
width={450}
height={445}
resizable>
height={445}>
<Window.Content scrollable>
<ApcContent />
</Window.Content>
@@ -86,7 +84,7 @@ const ApcContent = (props, context) => {
);
}
return (
<Fragment>
<>
<InterfaceLockNoticeBox />
<Section title="Power Status">
<LabeledList>
@@ -113,7 +111,7 @@ const ApcContent = (props, context) => {
color={chargingStatus.color}
buttons={(
<Button
icon={data.chargeMode ? 'sync' : 'close'}
icon={data.chargeMode ? 'sync' : 'times'}
content={data.chargeMode ? 'Auto' : 'Off'}
disabled={locked}
onClick={() => act('charge')} />
@@ -131,7 +129,7 @@ const ApcContent = (props, context) => {
key={channel.title}
label={channel.title}
buttons={(
<Fragment>
<>
<Box inline mx={2}
color={channel.status >= 2 ? 'good' : 'bad'}>
{channel.status >= 2 ? 'On' : 'Off'}
@@ -156,7 +154,7 @@ const ApcContent = (props, context) => {
selected={!locked && channel.status === 0}
disabled={locked}
onClick={() => act('channel', topicParams.off)} />
</Fragment>
</>
)}>
{channel.powerLoad}
</LabeledList.Item>
@@ -170,7 +168,7 @@ const ApcContent = (props, context) => {
<Section
title="Misc"
buttons={!!data.siliconUser && (
<Fragment>
<>
{!!data.malfStatus && (
<Button
icon={malfStatus.icon}
@@ -182,7 +180,7 @@ const ApcContent = (props, context) => {
icon="lightbulb-o"
content="Overload"
onClick={() => act('overload')} />
</Fragment>
</>
)}>
<LabeledList>
<LabeledList.Item
@@ -236,6 +234,6 @@ const ApcContent = (props, context) => {
)}
</LabeledList>
</Section>
</Fragment>
</>
);
};
+7 -9
View File
@@ -1,7 +1,6 @@
import { map, sortBy } from 'common/collections';
import { flow } from 'common/fp';
import { pureComponentHooks } from 'common/react';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Dimmer, Flex, Icon, Table, Tabs } from '../components';
import { Window } from '../layouts';
@@ -13,8 +12,7 @@ export const ApcControl = (props, context) => {
<Window
title="APC Controller"
width={550}
height={500}
resizable>
height={500}>
{data.authenticated === 1 && (
<ApcLoggedIn />
)}
@@ -48,7 +46,7 @@ const ApcLoggedIn = (props, context) => {
setTabIndex,
] = useLocalState(context, 'tab-index', 1);
return (
<Fragment>
<>
<Tabs>
<Tabs.Tab
selected={tabIndex === 1}
@@ -74,14 +72,14 @@ const ApcLoggedIn = (props, context) => {
</Dimmer>
)}
{tabIndex === 1 && (
<Fragment>
<>
<ControlPanel />
<Box fillPositionedParent top="53px">
<Window.Content scrollable>
<ApcControlScene />
</Window.Content>
</Box>
</Fragment>
</>
)}
{tabIndex === 2 && (
<Box fillPositionedParent top="20px">
@@ -90,7 +88,7 @@ const ApcLoggedIn = (props, context) => {
</Window.Content>
</Box>
)}
</Fragment>
</>
);
};
@@ -128,7 +126,7 @@ const ControlPanel = (props, context) => {
<Flex.Item grow={1} />
<Flex.Item>
{emagged === 1 && (
<Fragment>
<>
<Button
color={logging === 1 ? 'bad' : 'good'}
content={logging === 1 ? 'Stop Logging' : 'Restore Logging'}
@@ -138,7 +136,7 @@ const ControlPanel = (props, context) => {
content="Reset Console"
onClick={() => act('restore-console')}
/>
</Fragment>
</>
)}
<Button
color="bad"
@@ -9,8 +9,7 @@ export const AtmosAlertConsole = (props, context) => {
return (
<Window
width={350}
height={300}
resizable>
height={300}>
<Window.Content scrollable>
<Section title="Alarms">
<ul>
@@ -10,8 +10,7 @@ export const AtmosControlConsole = (props, context) => {
return (
<Window
width={500}
height={315}
resizable>
height={315}>
<Window.Content scrollable>
<Section
title={!!data.tank && sensors[0]?.long_name}>
@@ -18,8 +18,7 @@ export const AtmosControlPanel = (props, context) => {
<Window
title="SSAir Control Panel"
width={900}
height={500}
resizable>
height={500}>
<Section m={1}>
<Flex
justify="space-between"
@@ -22,7 +22,7 @@ export const AtmosTempPump = (props, context) => {
<NumberInput
animated
value={parseFloat(data.rate)}
unit="K/s"
unit="%"
width="75px"
minValue={0}
maxValue={data.max_heat_transfer_rate}
+1 -1
View File
@@ -44,7 +44,7 @@ export const Bepis = (props, context) => {
<Grid.Column size={1.5}>
<Section title="Stored Data and Statistics">
<LabeledList>
<LabeledList.Item label="Deposited Credits">
<LabeledList.Item label="Available Credits">
{data.stored_cash}
</LabeledList.Item>
<LabeledList.Item label="Investment Variability">
@@ -1,6 +1,5 @@
import { classes } from 'common/react';
import { createSearch } from 'common/string';
import { Fragment } from 'inferno';
import { useBackend, useLocalState } from '../backend';
import { Box, Button, Dimmer, Flex, Icon, Input, NoticeBox, NumberInput, Section, Table, Tabs } from '../components';
import { formatMoney } from '../format';
@@ -17,8 +16,7 @@ export const Biogenerator = (props, context) => {
return (
<Window
width={550}
height={420}
resizable>
height={420}>
{!!processing && (
<Dimmer fontSize="32px">
<Icon name="cog" spin={1} />
@@ -77,7 +75,7 @@ export const BiogeneratorContent = (props, context) => {
</Box>
)}
buttons={(
<Fragment>
<>
Search
<Input
autoFocus
@@ -93,7 +91,7 @@ export const BiogeneratorContent = (props, context) => {
content="Activate"
disabled={!can_process}
onClick={() => act('activate')} />
</Fragment>
</>
)}>
<Flex>
{searchText.length === 0 && (
@@ -1,5 +1,5 @@
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, Flex, Modal, Section, Tabs } from '../components';
import { AnimatedNumber, Box, Button, Flex, Modal, Section, Stack, Tabs } from '../components';
import { formatMoney } from '../format';
import { Window } from '../layouts';
@@ -15,10 +15,9 @@ export const BlackMarketUplink = (props, context) => {
} = data;
return (
<Window
width={800}
width={600}
height={480}
theme="hackerman"
resizable>
theme="hackerman">
<ShipmentSelector />
<Window.Content scrollable>
<Section
@@ -65,27 +64,27 @@ export const BlackMarketUplink = (props, context) => {
className="candystripe"
p={1}
pb={2}>
<Flex spacing={1} align="baseline">
<Flex.Item bold grow={1}>
<Stack align="baseline">
<Stack.Item grow bold>
{item.name}
</Flex.Item>
<Flex.Item color="label">
</Stack.Item>
<Stack.Item color="label">
{item.amount
? item.amount + " in stock"
: "Out of stock"}
</Flex.Item>
<Flex.Item>
</Stack.Item>
<Stack.Item>
{formatMoney(item.cost) + ' cr'}
</Flex.Item>
<Flex.Item>
</Stack.Item>
<Stack.Item>
<Button
content="Buy"
disabled={!item.amount || item.cost > money}
onClick={() => act('select', {
item: item.id,
})} />
</Flex.Item>
</Flex>
</Stack.Item>
</Stack>
{item.desc}
</Box>
))}
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, NoticeBox, Section } from '../components';
import { Window } from '../layouts';
@@ -22,7 +21,7 @@ export const BluespaceArtillery = (props, context) => {
</NoticeBox>
)}
{connected ? (
<Fragment>
<>
<Section
title="Target"
buttons={(
@@ -51,7 +50,7 @@ export const BluespaceArtillery = (props, context) => {
onClick={() => act('fire')} />
</Box>
) : (
<Fragment>
<>
<Box
color="bad"
fontSize="18px">
@@ -61,10 +60,10 @@ export const BluespaceArtillery = (props, context) => {
Awaiting authorization via keycard reader from at minimum
two station heads.
</Box>
</Fragment>
</>
)}
</Section>
</Fragment>
</>
) : (
<Section>
<LabeledList>
@@ -18,8 +18,7 @@ export const BluespaceLocator = (props, context) => {
return (
<Window
width={300}
height={300}
resizable>
height={300}>
<Window.Content scrollable>
<Tabs>
<Tabs.Tab
@@ -36,7 +36,7 @@ export const BluespaceSender = (props, context) => {
mr={0.5}
color="transparent"
icon="info"
tooltipPosition="bottom-left"
tooltipPosition="bottom-start"
tooltip={multiline`
Any gas you pipe into here will be added to the Bluespace
Network! That means any connected Bluespace Vendor (multitool)
@@ -59,13 +59,13 @@ export const BluespaceSender = (props, context) => {
icon={data.on ? 'power-off' : 'times'}
content={data.on ? 'On' : 'Off'}
selected={data.on}
tooltipPosition="bottom-left"
tooltipPosition="bottom-start"
tooltip="Will only take in gases while on."
onClick={() => act('power')} />
<Button
ml={0.5}
content="Retrieve gases"
tooltipPosition="bottom-left"
tooltipPosition="bottom-start"
tooltip="Will transfer any gases inside to the pipe."
onClick={() => act('retrieve')} />
</>
@@ -87,7 +87,7 @@ export const BluespaceVendor = (props, context) => {
<Button
color="transparent"
icon="info"
tooltipPosition="bottom-left"
tooltipPosition="bottom-start"
tooltip={multiline`
Quick guide for machine use: prepare a tank to create a
new one in the machine, pick how much you want it filled,
+2 -3
View File
@@ -16,8 +16,7 @@ export const BorgPanel = (props, context) => {
<Window
title="Borg Panel"
width={700}
height={700}
resizable>
height={700}>
<Window.Content scrollable>
<Section
title={borg.name}
@@ -83,7 +82,7 @@ export const BorgPanel = (props, context) => {
})} />
))}
</LabeledList.Item>
<LabeledList.Item label="Module">
<LabeledList.Item label="Model">
{modules.map(module => (
<Button
key={module.type}
+3 -5
View File
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Button, Section } from '../components';
import { Window } from '../layouts';
@@ -8,13 +7,12 @@ export const BrigTimer = (props, context) => {
return (
<Window
width={300}
height={138}
resizable>
height={138}>
<Window.Content scrollable>
<Section
title="Cell Timer"
buttons={(
<Fragment>
<>
<Button
icon="clock-o"
content={data.timing ? 'Stop' : 'Start'}
@@ -25,7 +23,7 @@ export const BrigTimer = (props, context) => {
content={data.flash_charging ? 'Recharging' : 'Flash'}
disabled={data.flash_charging}
onClick={() => act('flash')} />
</Fragment>
</>
)}>
<Button
icon="fast-backward"
@@ -51,8 +51,7 @@ export const CameraConsole = (props, context) => {
return (
<Window
width={870}
height={708}
resizable>
height={708}>
<div className="CameraConsole__left">
<Window.Content scrollable>
<CameraConsoleContent />
+15 -14
View File
@@ -1,5 +1,4 @@
import { toFixed } from 'common/math';
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, Flex, Icon, Knob, LabeledControls, LabeledList, RoundGauge, Section, Tooltip } from '../components';
import { formatSiUnit } from '../format';
@@ -40,7 +39,7 @@ export const Canister = (props, context) => {
<Section
title="Canister"
buttons={(
<Fragment>
<>
{!!isPrototype && (
<Button
mr={1}
@@ -55,7 +54,7 @@ export const Canister = (props, context) => {
icon="pencil-alt"
content="Relabel"
onClick={() => act('relabel')} />
</Fragment>
</>
)}>
<LabeledControls>
<LabeledControls.Item
@@ -127,17 +126,19 @@ export const Canister = (props, context) => {
<LabeledControls.Item
mr={1}
label="Port">
<Box position="relative">
<Icon
size={1.25}
name={portConnected ? 'plug' : 'times'}
color={portConnected ? 'good' : 'bad'} />
<Tooltip
content={portConnected
? 'Connected'
: 'Disconnected'}
position="top" />
</Box>
<Tooltip
content={portConnected
? 'Connected'
: 'Disconnected'}
position="top"
>
<Box position="relative">
<Icon
size={1.25}
name={portConnected ? 'plug' : 'times'}
color={portConnected ? 'good' : 'bad'} />
</Box>
</Tooltip>
</LabeledControls.Item>
</LabeledControls>
</Section>
+1 -2
View File
@@ -90,8 +90,7 @@ export const Canvas = (props, context) => {
return (
<Window
width={Math.min(700, width * dotsize + 72)}
height={Math.min(700, height * dotsize + 72)}
resizable>
height={Math.min(700, height * dotsize + 72)}>
<Window.Content>
<Box textAlign="center">
<PaintCanvas
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
@@ -10,8 +9,7 @@ export const CargoExpress = (props, context) => {
return (
<Window
width={600}
height={700}
resizable>
height={700}>
<Window.Content scrollable>
<InterfaceLockNoticeBox
accessText="a QM-level ID card" />
@@ -26,7 +24,7 @@ export const CargoExpress = (props, context) => {
const CargoExpressContent = (props, context) => {
const { act, data } = useBackend(context);
return (
<Fragment>
<>
<Section
title="Cargo Express"
buttons={(
@@ -59,6 +57,6 @@ const CargoExpressContent = (props, context) => {
</LabeledList>
</Section>
<CargoCatalog express />
</Fragment>
</>
);
};
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { AnimatedNumber, Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
@@ -14,8 +13,7 @@ export const CargoHoldTerminal = (props, context) => {
return (
<Window
width={600}
height={230}
resizable>
height={230}>
<Window.Content scrollable>
<Section>
<LabeledList>
@@ -29,7 +27,7 @@ export const CargoHoldTerminal = (props, context) => {
<Section
title="Cargo Pad"
buttons={(
<Fragment>
<>
<Button
icon={"sync"}
content={"Recalculate Value"}
@@ -41,7 +39,7 @@ export const CargoHoldTerminal = (props, context) => {
selected={sending}
disabled={!pad}
onClick={() => act(sending ? 'stop' : 'send')} />
</Fragment>
</>
)}>
<LabeledList>
<LabeledList.Item
@@ -1,4 +1,3 @@
import { Fragment } from 'inferno';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components';
import { Window } from '../layouts';
@@ -9,8 +8,7 @@ export const CellularEmporium = (props, context) => {
return (
<Window
width={900}
height={480}
resizable>
height={480}>
<Window.Content scrollable>
<Section>
<LabeledList>
@@ -35,7 +33,7 @@ export const CellularEmporium = (props, context) => {
className="candystripe"
label={ability.name}
buttons={(
<Fragment>
<>
{ability.dna_cost}
{' '}
<Button
@@ -44,7 +42,7 @@ export const CellularEmporium = (props, context) => {
onClick={() => act('evolve', {
name: ability.name,
})} />
</Fragment>
</>
)}>
{ability.desc}
<Box color="good">

Some files were not shown because too many files have changed in this diff Show More