Artur
2020-11-16 15:19:31 +02:00
parent bf69245a58
commit 6cdef62abb
21 changed files with 322 additions and 223 deletions

View File

@@ -2,6 +2,8 @@
"recommendations": [
"gbasood.byond-dm-language-support",
"platymuus.dm-langclient",
"EditorConfig.EditorConfig"
"EditorConfig.EditorConfig",
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint"
]
}

18
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,18 @@
{
"eslint.nodePath": "tgui/.yarn/sdks",
"eslint.workingDirectories": [
"./tgui"
],
"search.exclude": {
"tgui/.yarn": true,
"tgui/.pnp.*": true
},
"workbench.editorAssociations": [
{
"filenamePattern": "*.dmi",
"viewType": "imagePreview.previewEditor"
}
],
"files.eol": "\n",
"gitlens.advanced.blame.customArguments": ["-w"]
}

View File

@@ -71,21 +71,21 @@
// Generate page html
var/html = SStgui.basehtml
html = replacetextEx(html, "\[tgui:windowId]", id)
// Process inline assets
var/inline_styles = ""
var/inline_scripts = ""
// Inject inline assets
var/inline_assets_str = ""
for(var/datum/asset/asset in inline_assets)
var/mappings = asset.get_url_mappings()
for(var/name in mappings)
var/url = mappings[name]
// Not urlencoding since asset strings are considered safe
// Not encoding since asset strings are considered safe
if(copytext(name, -4) == ".css")
inline_styles += "<link rel=\"stylesheet\" type=\"text/css\" href=\"[url]\">\n"
inline_assets_str += "Byond.loadCss('[url]', true);\n"
else if(copytext(name, -3) == ".js")
inline_scripts += "<script type=\"text/javascript\" defer src=\"[url]\"></script>\n"
inline_assets_str += "Byond.loadJs('[url]', true);\n"
asset.send(client)
html = replacetextEx(html, "<!-- tgui:styles -->\n", inline_styles)
html = replacetextEx(html, "<!-- tgui:scripts -->\n", inline_scripts)
if(length(inline_assets_str))
inline_assets_str = "<script>\n" + inline_assets_str + "</script>\n"
html = replacetextEx(html, "<!-- tgui:assets -->\n", inline_assets_str)
// Inject custom HTML
html = replacetextEx(html, "<!-- tgui:html -->\n", inline_html)
// Open the window

20
tgui/.yarn/sdks/eslint/bin/eslint.js vendored Normal file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env node
const {existsSync} = require(`fs`);
const {createRequire, createRequireFromPath} = require(`module`);
const {resolve, dirname} = require(`path`);
const relPnpApiPath = "../../../../.pnp.js";
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
const absRequire = (createRequire || createRequireFromPath)(absPnpApiPath);
if (existsSync(absPnpApiPath)) {
if (!process.versions.pnp) {
// Setup the environment to be able to require eslint/bin/eslint.js
require(absPnpApiPath).setup();
}
}
// Defer to the real eslint/bin/eslint.js your application uses
module.exports = absRequire(`eslint/bin/eslint.js`);

6
tgui/.yarn/sdks/eslint/package.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "eslint",
"version": "7.4.0-pnpify",
"main": "./lib/api.js",
"type": "commonjs"
}

5
tgui/.yarn/sdks/integrations.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
# This file is automatically generated by PnPify.
# Manual changes will be lost!
integrations:
- vscode

12
tgui/global.d.ts vendored
View File

@@ -97,11 +97,21 @@ interface ByondType {
winset(id: string, propName: string, propValue: any): void;
/**
* Parses BYOND JSON
* Parses BYOND JSON.
*
* Uses a special encoding to preverse Infinity and NaN.
*/
parseJson(text: string): any;
/**
* Loads a stylesheet into the document.
*/
loadCss(url: string): void;
/**
* Loads a script into the document.
*/
loadJs(url: string): void;
}
declare const Byond: ByondType;

View File

@@ -30,7 +30,6 @@
@include meta.load-css('~tgui/styles/components/Dimmer.scss');
@include meta.load-css('~tgui/styles/components/Divider.scss');
@include meta.load-css('~tgui/styles/components/Dropdown.scss');
@include meta.load-css('~tgui/styles/components/FatalError.scss');
@include meta.load-css('~tgui/styles/components/Flex.scss');
@include meta.load-css('~tgui/styles/components/Input.scss');
@include meta.load-css('~tgui/styles/components/Knob.scss');

View File

@@ -4,68 +4,9 @@
* @license MIT
*/
import { loadCSS as fgLoadCss } from 'fg-loadcss';
import { createLogger } from './logging';
const logger = createLogger('assets');
const EXCLUDED_PATTERNS = [/v4shim/i];
const RETRY_ATTEMPTS = 5;
const RETRY_INTERVAL = 3000;
const loadedStyleSheetByUrl = {};
const loadedMappings = {};
export const loadStyleSheet = (url, attempt = 1) => {
if (loadedStyleSheetByUrl[url]) {
return;
}
loadedStyleSheetByUrl[url] = true;
logger.log(`loading stylesheet '${url}'`);
/** @type {HTMLLinkElement} */
let node = fgLoadCss(url);
node.addEventListener('load', () => {
if (!isStyleSheetReallyLoaded(node, url)) {
node.parentNode.removeChild(node);
node = null;
loadedStyleSheetByUrl[url] = null;
if (attempt >= RETRY_ATTEMPTS) {
logger.error(`Error: Failed to load the stylesheet `
+ `'${url}' after ${RETRY_ATTEMPTS} attempts.\nIt was either `
+ `not found, or you're trying to load an empty stylesheet `
+ `that has no CSS rules in it.`);
return;
}
setTimeout(() => loadStyleSheet(url, attempt + 1), RETRY_INTERVAL);
return;
}
});
};
/**
* Checks whether the stylesheet was registered in the DOM
* and is not empty.
*/
const isStyleSheetReallyLoaded = (node, url) => {
// Method #1 (works on IE10+)
const styleSheet = node.sheet;
if (styleSheet) {
return styleSheet.rules.length > 0;
}
// Method #2
const styleSheets = document.styleSheets;
const len = styleSheets.length;
for (let i = 0; i < len; i++) {
const styleSheet = styleSheets[i];
if (styleSheet.href.includes(url)) {
return styleSheet.rules.length > 0;
}
}
// All methods failed
logger.warn(`Warning: stylesheet '${url}' was not found in the DOM`);
return false;
};
export const resolveAsset = name => (
loadedMappings[name] || name
);
@@ -73,7 +14,7 @@ export const resolveAsset = name => (
export const assetMiddleware = store => next => action => {
const { type, payload } = action;
if (type === 'asset/stylesheet') {
loadStyleSheet(payload);
Byond.loadCss(payload);
return;
}
if (type === 'asset/mappings') {
@@ -86,7 +27,10 @@ export const assetMiddleware = store => next => action => {
const ext = name.split('.').pop();
loadedMappings[name] = url;
if (ext === 'css') {
loadStyleSheet(url);
Byond.loadCss(url);
}
if (ext === 'js') {
Byond.loadJs(url);
}
}
return;

View File

@@ -189,6 +189,9 @@ export const backendMiddleware = store => {
// Resume on incoming update
if (type === 'backend/update' && suspended) {
// Show the payload
logger.log('backend/update', payload);
// Signal renderer that we have resumed
resumeRenderer();
// Setup drag
setupDrag();

View File

@@ -24,7 +24,7 @@ export const debugMiddleware = store => {
if (key.code === KEY_F12) {
store.dispatch(toggleKitchenSink());
}
if (key.ctrl && key.shift && key.code === KEY_BACKSPACE) {
if (key.ctrl && key.alt && key.code === KEY_BACKSPACE) {
// NOTE: We need to call this in a timeout, because we need a clean
// stack in order for this to be a fatal error.
setTimeout(() => {

View File

@@ -4,8 +4,7 @@
"version": "4.2.0",
"dependencies": {
"common": "workspace:*",
"dompurify": "^2.0.11",
"fg-loadcss": "^2.1.0",
"dompurify": "^2.0.12",
"inferno": "^7.4.2",
"inferno-vnode-flags": "^7.4.2",
"marked": "^1.1.0",

View File

@@ -1,97 +0,0 @@
/**
* Copyright (c) 2020 Aleksej Komarov
* SPDX-License-Identifier: MIT
*/
.FatalError {
display: block !important;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 12px;
font-size: 12px;
font-family: Consolas, monospace;
color: #ffffff;
background-color: #0000dd;
z-index: 1000;
overflow: hidden;
text-align: center;
}
.FatalError__logo {
display: inline-block;
text-align: left;
font-size: 10px;
line-height: 8px;
position: relative;
margin-top: 12px;
top: 0;
left: 0;
animation:
FatalError__rainbow 2s linear infinite alternate,
FatalError__shadow 4s linear infinite alternate,
FatalError__tfmX 3s infinite alternate,
FatalError__tfmY 4s infinite alternate;
white-space: pre-wrap;
word-break: break-all;
}
.FatalError__header {
margin-top: 12px;
}
.FatalError__stack {
text-align: left;
white-space: pre-wrap;
word-break: break-all;
margin-top: 24px;
margin-bottom: 24px;
}
.FatalError__footer {
margin-bottom: 24px;
}
@keyframes FatalError__rainbow {
0% {
color: #ff0;
}
50% {
color: #0ff;
}
100% {
color: #f0f;
}
}
@keyframes FatalError__shadow {
0% {
left: -2px;
text-shadow: 4px 0 #f0f;
}
50% {
left: 0px;
text-shadow: 0px 0 #0ff;
}
100% {
left: 2px;
text-shadow: -4px 0 #ff0;
}
}
@keyframes FatalError__tfmX {
0% {
left: 15px;
}
100% {
left: -15px;
}
}
@keyframes FatalError__tfmY {
100% {
top: -15px;
}
}

View File

@@ -23,7 +23,6 @@
@include meta.load-css('./components/Dimmer.scss');
@include meta.load-css('./components/Divider.scss');
@include meta.load-css('./components/Dropdown.scss');
@include meta.load-css('./components/FatalError.scss');
@include meta.load-css('./components/Flex.scss');
@include meta.load-css('./components/Input.scss');
@include meta.load-css('./components/Knob.scss');

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -17,9 +17,7 @@ if (window.__windowId__ === '[' + 'tgui:windowId' + ']') {
window.__windowId__ = null;
}
// BYOND API object
window.Byond = (function () {
var Byond = {};
(function () {
// Utility functions
var hasOwn = Object.prototype.hasOwnProperty;
var assign = function (target) {
@@ -33,6 +31,11 @@ window.Byond = (function () {
}
return target;
};
// BYOND API object
// ------------------------------------------------------
var Byond = window.Byond = {};
// Trident engine version
var tridentVersion = (function () {
var groups = navigator.userAgent.match(/Trident\/(\d+).+?;/i);
@@ -170,7 +173,135 @@ window.Byond = (function () {
}
};
return Byond;
// Asset loaders
// ------------------------------------------------------
var RETRY_ATTEMPTS = 5;
var RETRY_INTERVAL = 2000;
var loadedCssByUrl = {};
var loadedJsByUrl = {};
var isStyleSheetLoaded = function (node, url) {
// Method #1 (works on IE10+)
var styleSheet = node.sheet;
if (styleSheet) {
return styleSheet.rules.length > 0;
}
// Method #2
var styleSheets = document.styleSheets;
var len = styleSheets.length;
for (var i = 0; i < len; i++) {
var styleSheet = styleSheets[i];
if (styleSheet.href.indexOf(url) !== -1) {
return styleSheet.rules.length > 0;
}
}
// All methods failed
return false;
};
var onDocumentBodyReady = function (done) {
if (document.body) {
return done();
}
setTimeout(function () {
onDocumentBodyReady(done);
});
};
var getRefNode = function () {
var refs = (document.body || document.getElementsByTagName('head')[0])
.childNodes;
return refs[refs.length - 1];
};
// Based on fg-loadcss package
// See: https://github.com/filamentgroup/loadCSS
Byond.loadCss = function (url, sync, attempt) {
if (loadedCssByUrl[url]) {
return;
}
if (!attempt) {
attempt = 1;
}
loadedCssByUrl[url] = true;
// Inject the stylesheet
var ref = getRefNode();
/** @type {HTMLLinkElement} */
var node = document.createElement('link');
node.type = 'text/css';
node.rel = 'stylesheet';
node.href = url;
// Temporarily set media to something inapplicable
// to ensure it'll fetch without blocking render
if (!sync) {
node.media = 'only x';
}
onDocumentBodyReady(function () {
ref.parentNode.insertBefore(node, ref.nextSibling);
});
// Listen for the load event
node.onload = function () {
node.onload = null;
if (isStyleSheetLoaded(node, url)) {
// Render the stylesheet
node.media = 'all';
return;
}
// Try again
node.parentNode.removeChild(node);
node = null;
loadedCssByUrl[url] = null;
if (attempt >= RETRY_ATTEMPTS) {
throw new Error("Error: Failed to load the stylesheet "
+ "'" + url + "' after " + RETRY_ATTEMPTS + " attempts.\n"
+ "It was either not found, or you're trying to load "
+ "an empty stylesheet that has no CSS rules in it.");
}
setTimeout(function () {
Byond.loadCss(url, sync, attempt + 1);
}, RETRY_INTERVAL);
};
};
Byond.loadJs = function (url, sync, attempt) {
if (loadedJsByUrl[url]) {
return;
}
if (!attempt) {
attempt = 1;
}
loadedJsByUrl[url] = true;
// Inject the stylesheet
var ref = getRefNode();
var node = document.createElement('script');
node.type = 'text/javascript';
if (sync) {
node.defer = true;
}
else {
node.async = true;
}
node.src = url;
onDocumentBodyReady(function () {
ref.parentNode.insertBefore(node, ref.nextSibling);
});
node.onerror = function () {
node.onerror = null;
node.parentNode.removeChild(node);
node = null;
loadedJsByUrl[url] = null;
if (attempt >= RETRY_ATTEMPTS) {
throw new Error("Error: Failed to load the script "
+ "'" + url + "' after " + RETRY_ATTEMPTS + " attempts.");
}
setTimeout(function () {
Byond.loadJs(url, sync, attempt + 1);
}, RETRY_INTERVAL);
};
};
})();
// Global error handling
@@ -190,7 +321,7 @@ window.onerror = function (msg, url, line, col, error) {
var errorRoot = document.getElementById('FatalError');
var errorStack = document.getElementById('FatalError__stack');
if (errorRoot) {
errorRoot.className = 'FatalError';
errorRoot.className = 'FatalError FatalError--visible';
if (errorStack.textContent) {
errorStack.textContent += '\n\n' + stack;
}
@@ -263,15 +394,97 @@ if (!Function.prototype.bind) (function () {
})();
</script>
<!-- Styles -->
<!-- tgui:styles -->
<style>
.FatalError {
display: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 12px;
font-size: 12px;
font-family: Consolas, monospace;
color: #ffffff;
background-color: #0000dd;
z-index: 1000;
overflow: hidden;
text-align: center;
}
<!-- Scripts -->
<!-- tgui:scripts -->
.FatalError--visible {
display: block !important;
}
.FatalError__logo {
display: inline-block;
text-align: left;
font-size: 10px;
line-height: 12px;
position: relative;
margin: 16px;
top: 0;
left: 0;
animation:
FatalError__rainbow 2s linear infinite alternate,
FatalError__shadow 4s linear infinite alternate,
FatalError__tfmX 3s infinite alternate,
FatalError__tfmY 4s infinite alternate;
white-space: pre;
}
.FatalError__header {
margin-top: 12px;
}
.FatalError__stack {
text-align: left;
white-space: pre-wrap;
word-break: break-all;
margin-top: 24px;
margin-bottom: 24px;
}
.FatalError__footer {
margin-bottom: 24px;
}
@keyframes FatalError__rainbow {
0% { color: #ff0; }
50% { color: #0ff; }
100% { color: #f0f; }
}
@keyframes FatalError__shadow {
0% {
left: -2px;
text-shadow: 4px 0 #f0f;
}
50% {
left: 0px;
text-shadow: 0px 0 #0ff;
}
100% {
left: 2px;
text-shadow: -4px 0 #ff0;
}
}
@keyframes FatalError__tfmX {
0% { left: 15px; }
100% { left: -15px; }
}
@keyframes FatalError__tfmY {
100% { top: -15px; }
}
</style>
</head>
<body>
<!-- Inline assets -->
<!-- tgui:assets -->
<!-- Inline HTML -->
<!-- tgui:html -->
@@ -279,36 +492,22 @@ if (!Function.prototype.bind) (function () {
<div id="react-root"></div>
<!-- Fatal error container -->
<div id="FatalError" style="display: none">
<div id="FatalError" class="FatalError">
<div class="FatalError__logo">
_____ _____ _______ _____
/\ \ /\ \ /::\ \ /\ \
/::\____\ /::\ \ /::::\ \ /::\ \
/::::| | \:::\ \ /::::::\ \ /::::\ \
/:::::| | \:::\ \ /::::::::\ \ /::::::\ \
/::::::| | \:::\ \ /:::/~~\:::\ \ /:::/\:::\ \
/:::/|::| | \:::\ \ /:::/ \:::\ \ /:::/__\:::\ \
/:::/ |::| | /::::\ \ /:::/ / \:::\ \ \:::\ \:::\ \
/:::/ |::| | _____ /::::::\ \ /:::/____/ \:::\____\ ___\:::\ \:::\ \
/:::/ |::| |/\ \ /:::/\:::\ \|:::| | |:::| |/\ \:::\ \:::\ \
/:: / |::| /::\____\/:::/ \:::\____\:::|____| |:::| /::\ \:::\ \:::\____\
\::/ /|::| /:::/ /:::/ \::/ /\:::\ \ /:::/ /\:::\ \:::\ \::/ /
\/____/ |::| /:::/ /:::/ / \/____/ \:::\ \ /:::/ / \:::\ \:::\ \/____/
|::|/:::/ /:::/ / \:::\ /:::/ / \:::\ \:::\ \
|::::::/ /:::/ / \:::\__/:::/ / \:::\ \:::\____\
|:::::/ /\::/ / \::::::::/ / \:::\ /:::/ /
|::::/ / \/____/ \::::::/ / \:::\/:::/ /
/:::/ / \::::/ / \::::::/ /
/:::/ / \::/____/ \::::/ /
\::/ / \::/ /
\/____/ \/____/
ooooo ooo . .oooooo. .oooooo..o
`888b. `8' .o8 d8P' `Y8b d8P' `Y8
8 `88b. 8 .o888oo 888 888 Y88bo.
8 `88b. 8 888 888 888 `"Y8888o.
8 `88b.8 888 888 888 `"Y88b
8 `888 888 . `88b d88' oo .d8P
o8o `8 "888" `Y8bood8P' 8""88888P'
</div>
<marquee class="FatalError__header">
A fatal exception has occurred at 002B:C562F1B7 in TGUI.
The current application will be terminated.
Please remain calm. Get to the nearest NTNet workstation
and send the copy of the following stack trace to:
www.github.com/tgstation/tgstation. Thank you for your cooperation.
www.github.com/Citadel-Station-13/Citadel-Station-13/issues. Thank you for your cooperation.
</marquee>
<div id="FatalError__stack" class="FatalError__stack"></div>
<div class="FatalError__footer">

View File

@@ -3062,10 +3062,10 @@ __metadata:
languageName: node
linkType: hard
"dompurify@npm:^2.0.11":
version: 2.0.12
resolution: "dompurify@npm:2.0.12"
checksum: 72fe758306be02d95c7ac51cecaf9d887dea6291cd83012b1c251d21912545c18d6ccc7140b84da2278a6910517cd1e996258991c7e3dda9b6da34f0b6040b91
"dompurify@npm:^2.0.12":
version: 2.2.2
resolution: "dompurify@npm:2.2.2"
checksum: eab7b8763c56256bb4fb009e59b814f750fe0a4be40f996f037f136a40dbe8107820989c7b548b92263076fce7023ee31c58a11ad479cba16b4626b8d91559f1
languageName: node
linkType: hard
@@ -3637,13 +3637,6 @@ __metadata:
languageName: node
linkType: hard
"fg-loadcss@npm:^2.1.0":
version: 2.1.0
resolution: "fg-loadcss@npm:2.1.0"
checksum: 5672a437bd51ba418ffa3c304394a4f85d6b53ae2bbcec2ef54d6498642af4e2c11a9902a55bf95cd96e7e787483c2087f56569484a3120a4228d880ffec6309
languageName: node
linkType: hard
"figgy-pudding@npm:^3.5.1":
version: 3.5.2
resolution: "figgy-pudding@npm:3.5.2"
@@ -8155,8 +8148,7 @@ fsevents@~2.1.2:
resolution: "tgui@workspace:packages/tgui"
dependencies:
common: "workspace:*"
dompurify: ^2.0.11
fg-loadcss: ^2.1.0
dompurify: ^2.0.12
inferno: ^7.4.2
inferno-vnode-flags: ^7.4.2
marked: ^1.1.0