diff --git a/code/controllers/subsystems/tgui.dm b/code/controllers/subsystems/tgui.dm index 054a55fcf6..a03e3452c3 100644 --- a/code/controllers/subsystems/tgui.dm +++ b/code/controllers/subsystems/tgui.dm @@ -26,6 +26,17 @@ SUBSYSTEM_DEF(tgui) /datum/controller/subsystem/tgui/PreInit() basehtml = file2text('tgui/public/tgui.html') + + // Inject inline helper functions + var/helpers = file2text('tgui/public/helpers.min.js') + helpers = "" + basehtml = replacetextEx(basehtml, "", helpers) + + // Inject inline ntos-error styles + var/ntos_error = file2text('tgui/public/ntos-error.min.css') + ntos_error = "" + basehtml = replacetextEx(basehtml, "", ntos_error) + basehtml = replacetextEx(basehtml, "", "Nanotrasen (c) 2284-[CURRENT_STATION_YEAR]") /datum/controller/subsystem/tgui/Shutdown() diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index f83d8b6546..c4c267faa3 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -62,7 +62,7 @@ var/atom/movable/AM = WF.resolve() if(isnull(AM)) log_debug("DEBUG: HasProximity called without reference on [src].") - attached_device?.HasProximity(T, WEAKREF(AM), old_loc) + attached_device?.HasProximity(T, WF, old_loc) /obj/item/transfer_valve/Moved(old_loc, direction, forced) . = ..() diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 8f3eec7abb..3df6f5f4b2 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -661,11 +661,11 @@ var/list/global/tank_gauge_cache = list() SIGNAL_HANDLER if(isnull(WF)) return - var/atom/movable/AM = WF + var/atom/movable/AM = WF.resolve() if(isnull(AM)) log_debug("DEBUG: HasProximity called without reference on [src].") return - assembly?.HasProximity(T, WEAKREF(AM), old_loc) + assembly?.HasProximity(T, WF, old_loc) /obj/item/tankassemblyproxy/Moved(old_loc, direction, forced) if(isturf(old_loc)) diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 88be1df8ac..2104b26164 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -79,9 +79,9 @@ log_debug("DEBUG: HasProximity called without reference on [src].") return if(a_left) - a_left.HasProximity(T, AM, old_loc) + a_left.HasProximity(T, WF, old_loc) if(a_right) - a_right.HasProximity(T, AM, old_loc) + a_right.HasProximity(T, WF, old_loc) /obj/item/assembly_holder/Crossed(atom/movable/AM as mob|obj) if(AM.is_incorporeal()) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/plants.dm b/code/modules/mob/living/simple_mob/subtypes/vore/plants.dm index 2d7b37e0a0..075c29f648 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/plants.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/plants.dm @@ -82,10 +82,7 @@ return if(L.devourable && L.allowmobvore && (src.vore_fullness < src.vore_capacity)) perform_the_nom(src,L,src,src.vore_selected,1) - L |= eaten_mobs - return - else - return + eaten_mobs += L ////////////////////////////PITCHER PLANT//////////////////////////////////////////////// diff --git a/tgui/.gitignore b/tgui/.gitignore index 55091b2186..75819f5263 100644 --- a/tgui/.gitignore +++ b/tgui/.gitignore @@ -16,6 +16,8 @@ package-lock.json /public/.tmp/**/* /public/**/* !/public/*.html +!/public/ntos-error.min.css +!/public/helpers.min.js /coverage ## Previously ignored locations that are kept to avoid confusing git diff --git a/tgui/.prettierignore b/tgui/.prettierignore index 377200973d..7a31134788 100644 --- a/tgui/.prettierignore +++ b/tgui/.prettierignore @@ -8,7 +8,6 @@ .swcrc /docs -/public /packages/tgui-polyfill /packages/tgfont/static /packages/tgfont/dist @@ -16,6 +15,7 @@ **/*.yml **/*.md -## Build artifacts -/public/.tmp/**/* -/public/*.map +# Avoid running on any bundles. +/public/**/* +# Running it on tgui.html is fine, however. +!/public/tgui.html diff --git a/tgui/.prettierrc.yml b/tgui/.prettierrc.yml index 0176969226..64d6436bcd 100644 --- a/tgui/.prettierrc.yml +++ b/tgui/.prettierrc.yml @@ -1 +1,6 @@ singleQuote: true +overrides: + - files: 'packages/tgui-setup/helpers.js' + options: + trailingComma: es5 + arrowParens: always diff --git a/tgui/.yarn/sdks/eslint/bin/eslint.js b/tgui/.yarn/sdks/eslint/bin/eslint.js index f0557a0776..090bd783c2 100644 --- a/tgui/.yarn/sdks/eslint/bin/eslint.js +++ b/tgui/.yarn/sdks/eslint/bin/eslint.js @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require eslint/bin/eslint.js require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real eslint/bin/eslint.js your application uses -module.exports = absRequire(`eslint/bin/eslint.js`); +module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`)); diff --git a/tgui/.yarn/sdks/eslint/lib/api.js b/tgui/.yarn/sdks/eslint/lib/api.js index 93f1f0c282..93da8ae23d 100644 --- a/tgui/.yarn/sdks/eslint/lib/api.js +++ b/tgui/.yarn/sdks/eslint/lib/api.js @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require eslint require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real eslint your application uses -module.exports = absRequire(`eslint`); +module.exports = wrapWithUserWrapper(absRequire(`eslint`)); diff --git a/tgui/.yarn/sdks/eslint/lib/unsupported-api.js b/tgui/.yarn/sdks/eslint/lib/unsupported-api.js index 01e7653dcc..c2b464ce69 100644 --- a/tgui/.yarn/sdks/eslint/lib/unsupported-api.js +++ b/tgui/.yarn/sdks/eslint/lib/unsupported-api.js @@ -1,20 +1,32 @@ #!/usr/bin/env node -const { existsSync } = require(`fs`); -const { createRequire } = require(`module`); -const { resolve } = require(`path`); +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); -const relPnpApiPath = '../../../../.pnp.cjs'; +const relPnpApiPath = "../../../../.pnp.cjs"; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require eslint/use-at-your-own-risk require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real eslint/use-at-your-own-risk your application uses -module.exports = absRequire(`eslint/use-at-your-own-risk`); +module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`)); diff --git a/tgui/.yarn/sdks/eslint/package.json b/tgui/.yarn/sdks/eslint/package.json index 263cd3d500..4110fb08c3 100644 --- a/tgui/.yarn/sdks/eslint/package.json +++ b/tgui/.yarn/sdks/eslint/package.json @@ -1,6 +1,6 @@ { "name": "eslint", - "version": "8.57.0-sdk", + "version": "8.57.1-sdk", "main": "./lib/api.js", "type": "commonjs", "bin": { diff --git a/tgui/.yarn/sdks/prettier/bin/prettier.cjs b/tgui/.yarn/sdks/prettier/bin/prettier.cjs index 99a6b3e602..2f0197037d 100644 --- a/tgui/.yarn/sdks/prettier/bin/prettier.cjs +++ b/tgui/.yarn/sdks/prettier/bin/prettier.cjs @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require prettier/bin/prettier.cjs require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real prettier/bin/prettier.cjs your application uses -module.exports = absRequire(`prettier/bin/prettier.cjs`); +module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`)); diff --git a/tgui/.yarn/sdks/prettier/index.cjs b/tgui/.yarn/sdks/prettier/index.cjs index 27ef078785..e6d3f4a3bd 100644 --- a/tgui/.yarn/sdks/prettier/index.cjs +++ b/tgui/.yarn/sdks/prettier/index.cjs @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require prettier require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real prettier your application uses -module.exports = absRequire(`prettier`); +module.exports = wrapWithUserWrapper(absRequire(`prettier`)); diff --git a/tgui/.yarn/sdks/prettier/package.json b/tgui/.yarn/sdks/prettier/package.json index 6e68f38b67..ad2a6d3baa 100644 --- a/tgui/.yarn/sdks/prettier/package.json +++ b/tgui/.yarn/sdks/prettier/package.json @@ -1,6 +1,6 @@ { "name": "prettier", - "version": "3.2.5-sdk", + "version": "3.5.3-sdk", "main": "./index.cjs", "type": "commonjs", "bin": "./bin/prettier.cjs" diff --git a/tgui/.yarn/sdks/typescript/bin/tsc b/tgui/.yarn/sdks/typescript/bin/tsc index 815aedd29d..590667f936 100644 --- a/tgui/.yarn/sdks/typescript/bin/tsc +++ b/tgui/.yarn/sdks/typescript/bin/tsc @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require typescript/bin/tsc require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real typescript/bin/tsc your application uses -module.exports = absRequire(`typescript/bin/tsc`); +module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`)); diff --git a/tgui/.yarn/sdks/typescript/bin/tsserver b/tgui/.yarn/sdks/typescript/bin/tsserver index 7d9de68016..d178cbe38c 100644 --- a/tgui/.yarn/sdks/typescript/bin/tsserver +++ b/tgui/.yarn/sdks/typescript/bin/tsserver @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require typescript/bin/tsserver require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real typescript/bin/tsserver your application uses -module.exports = absRequire(`typescript/bin/tsserver`); +module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`)); diff --git a/tgui/.yarn/sdks/typescript/lib/tsc.js b/tgui/.yarn/sdks/typescript/lib/tsc.js index 1d3e2a83cf..95044b6adc 100644 --- a/tgui/.yarn/sdks/typescript/lib/tsc.js +++ b/tgui/.yarn/sdks/typescript/lib/tsc.js @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require typescript/lib/tsc.js require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real typescript/lib/tsc.js your application uses -module.exports = absRequire(`typescript/lib/tsc.js`); +module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`)); diff --git a/tgui/.yarn/sdks/typescript/lib/tsserver.js b/tgui/.yarn/sdks/typescript/lib/tsserver.js index 383f27cf13..55dc0955b6 100644 --- a/tgui/.yarn/sdks/typescript/lib/tsserver.js +++ b/tgui/.yarn/sdks/typescript/lib/tsserver.js @@ -1,15 +1,48 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +<<<<<<< HEAD const moduleWrapper = (tsserver) => { +======= +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsserver.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +const moduleWrapper = exports => { + return wrapWithUserWrapper(moduleWrapperFn(exports)); +}; + +const moduleWrapperFn = tsserver => { +>>>>>>> cc02a1a056 (split tgui html (#17476)) if (!process.versions.pnp) { return tsserver; } @@ -261,11 +294,11 @@ const moduleWrapper = (tsserver) => { return tsserver; }; -if (existsSync(absPnpApiPath)) { - if (!process.versions.pnp) { - // Setup the environment to be able to require typescript/lib/tsserver.js - require(absPnpApiPath).setup(); - } +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); +// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. +// Ref https://github.com/microsoft/TypeScript/pull/55326 +if (major > 5 || (major === 5 && minor >= 5)) { + moduleWrapper(absRequire(`typescript`)); } // Defer to the real typescript/lib/tsserver.js your application uses diff --git a/tgui/.yarn/sdks/typescript/lib/tsserverlibrary.js b/tgui/.yarn/sdks/typescript/lib/tsserverlibrary.js index 28fe36fcc6..44c4d3ff54 100644 --- a/tgui/.yarn/sdks/typescript/lib/tsserverlibrary.js +++ b/tgui/.yarn/sdks/typescript/lib/tsserverlibrary.js @@ -1,15 +1,48 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +<<<<<<< HEAD const moduleWrapper = (tsserver) => { +======= +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + +if (existsSync(absPnpApiPath)) { + if (!process.versions.pnp) { + // Setup the environment to be able to require typescript/lib/tsserverlibrary.js + require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } + } +} + +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + +const moduleWrapper = exports => { + return wrapWithUserWrapper(moduleWrapperFn(exports)); +}; + +const moduleWrapperFn = tsserver => { +>>>>>>> cc02a1a056 (split tgui html (#17476)) if (!process.versions.pnp) { return tsserver; } @@ -261,11 +294,11 @@ const moduleWrapper = (tsserver) => { return tsserver; }; -if (existsSync(absPnpApiPath)) { - if (!process.versions.pnp) { - // Setup the environment to be able to require typescript/lib/tsserverlibrary.js - require(absPnpApiPath).setup(); - } +const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10)); +// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well. +// Ref https://github.com/microsoft/TypeScript/pull/55326 +if (major > 5 || (major === 5 && minor >= 5)) { + moduleWrapper(absRequire(`typescript`)); } // Defer to the real typescript/lib/tsserverlibrary.js your application uses diff --git a/tgui/.yarn/sdks/typescript/lib/typescript.js b/tgui/.yarn/sdks/typescript/lib/typescript.js index 3ebd073e31..8ee41f7542 100644 --- a/tgui/.yarn/sdks/typescript/lib/typescript.js +++ b/tgui/.yarn/sdks/typescript/lib/typescript.js @@ -1,20 +1,38 @@ #!/usr/bin/env node +<<<<<<< HEAD const { existsSync } = require(`fs`); const { createRequire } = require(`module`); const { resolve } = require(`path`); +======= +const {existsSync} = require(`fs`); +const {createRequire, register} = require(`module`); +const {resolve} = require(`path`); +const {pathToFileURL} = require(`url`); +>>>>>>> cc02a1a056 (split tgui html (#17476)) const relPnpApiPath = '../../../../.pnp.cjs'; const absPnpApiPath = resolve(__dirname, relPnpApiPath); +const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`); const absRequire = createRequire(absPnpApiPath); +const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`); +const isPnpLoaderEnabled = existsSync(absPnpLoaderPath); + if (existsSync(absPnpApiPath)) { if (!process.versions.pnp) { // Setup the environment to be able to require typescript require(absPnpApiPath).setup(); + if (isPnpLoaderEnabled && register) { + register(pathToFileURL(absPnpLoaderPath)); + } } } +const wrapWithUserWrapper = existsSync(absUserWrapperPath) + ? exports => absRequire(absUserWrapperPath)(exports) + : exports => exports; + // Defer to the real typescript your application uses -module.exports = absRequire(`typescript`); +module.exports = wrapWithUserWrapper(absRequire(`typescript`)); diff --git a/tgui/packages/tgui-setup/helpers.js b/tgui/packages/tgui-setup/helpers.js new file mode 100644 index 0000000000..076faf13b9 --- /dev/null +++ b/tgui/packages/tgui-setup/helpers.js @@ -0,0 +1,573 @@ +/* eslint-disable */ + +(function () { + // Utility functions + let hasOwn = Object.prototype.hasOwnProperty; + let assign = function (target) { + for (let i = 1; i < arguments.length; i++) { + let source = arguments[i]; + for (let key in source) { + if (hasOwn.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }; + let parseMetaTag = function (name) { + let content = document.getElementById(name).getAttribute('content'); + if (content === '[' + name + ']') { + return null; + } + return content; + }; + + // BYOND API object + // ------------------------------------------------------ + + let Byond = (window.Byond = {}); + + // Expose inlined metadata + Byond.windowId = parseMetaTag('tgui:windowId'); + + // Backwards compatibility + window.__windowId__ = Byond.windowId; + + // Trident engine version + Byond.TRIDENT = (function () { + let groups = navigator.userAgent.match(/Trident\/(\d+).+?;/i); + let majorVersion = groups && groups[1]; + return majorVersion ? parseInt(majorVersion, 10) : null; + })(); + + // Blink engine version + Byond.BLINK = (function () { + let groups = navigator.userAgent.match(/Chrome\/(\d+)\./); + let majorVersion = groups && groups[1]; + return majorVersion ? parseInt(majorVersion, 10) : null; + })(); + + // Basic checks to detect whether this page runs in BYOND + let isByond = + (Byond.TRIDENT !== null || Byond.BLINK !== null || window.cef_to_byond) && + location.hostname === '127.0.0.1' && + location.search !== '?external'; + // As of BYOND 515 the path doesn't seem to include tmp dir anymore if you're trying to open tgui in external browser and looking why it doesn't work + // && location.pathname.indexOf('/tmp') === 0 + + // Version constants + Byond.IS_BYOND = isByond; + + // Strict mode flag + Byond.strictMode = Boolean(Number(parseMetaTag('tgui:strictMode'))); + + // Callbacks for asynchronous calls + Byond.__callbacks__ = []; + + // Reviver for BYOND JSON + let byondJsonReviver = function (key, value) { + if (typeof value === 'object' && value !== null && value.__number__) { + return parseFloat(value.__number__); + } + return value; + }; + + // Makes a BYOND call. + // See: https://secure.byond.com/docs/ref/skinparams.html + Byond.call = function (path, params) { + // Not running in BYOND, abort. + if (!isByond) { + return; + } + // Build the URL + let url = (path || '') + '?'; + let i = 0; + if (params) { + for (let key in params) { + if (hasOwn.call(params, key)) { + if (i++ > 0) { + url += '&'; + } + let value = params[key]; + if (value === null || value === undefined) { + value = ''; + } + url += encodeURIComponent(key) + '=' + encodeURIComponent(value); + } + } + } + + // If we're a Chromium client, just use the fancy method + if (window.cef_to_byond) { + cef_to_byond('byond://' + url); + return; + } + + // Perform a standard call via location.href + if (url.length < 2048) { + location.href = 'byond://' + url; + return; + } + // Send an HTTP request to DreamSeeker's HTTP server. + // Allows sending much bigger payloads. + let xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.send(); + }; + + Byond.callAsync = function (path, params) { + if (!window.Promise) { + throw new Error('Async calls require API level of ES2015 or later.'); + } + let index = Byond.__callbacks__.length; + let promise = new window.Promise((resolve) => { + Byond.__callbacks__.push(resolve); + }); + Byond.call( + path, + assign({}, params, { + callback: 'Byond.__callbacks__[' + index + ']', + }) + ); + return promise; + }; + + Byond.topic = function (params) { + return Byond.call('', params); + }; + + Byond.command = function (command) { + return Byond.call('winset', { + command: command, + }); + }; + + Byond.winget = function (id, propName) { + if (id === null) { + id = ''; + } + let isArray = propName instanceof Array; + let isSpecific = propName && propName !== '*' && !isArray; + let promise = Byond.callAsync('winget', { + id: id, + property: (isArray && propName.join(',')) || propName || '*', + }); + if (isSpecific) { + promise = promise.then((props) => { + return props[propName]; + }); + } + return promise; + }; + + Byond.winset = function (id, propName, propValue) { + if (id === null) { + id = ''; + } else if (typeof id === 'object') { + return Byond.call('winset', id); + } + let props = {}; + if (typeof propName === 'string') { + props[propName] = propValue; + } else { + assign(props, propName); + } + props.id = id; + return Byond.call('winset', props); + }; + + Byond.parseJson = function (json) { + try { + return JSON.parse(json, byondJsonReviver); + } catch (err) { + throw new Error('JSON parsing error: ' + (err && err.message)); + } + }; + + let MAX_PACKET_SIZE = 1024; + + Byond.sendMessage = function (type, payload) { + let message = + typeof type === 'string' ? { type: type, payload: payload } : type; + // JSON-encode the payload + + if (message.payload !== null && message.payload !== undefined) { + message.payload = JSON.stringify(message.payload); + + if (!Byond.TRIDENT && message.payload.length > MAX_PACKET_SIZE) { + let chunks = []; + + for ( + let i = 0, charsLength = message.payload.length; + i < charsLength; + i += MAX_PACKET_SIZE + ) { + chunks.push(message.payload.substring(i, i + MAX_PACKET_SIZE)); + } + + for (let i = 0; i < chunks.length; i++) { + let to_send = chunks[i]; + + message = { + type: type, + packet: to_send, + packetId: i + 1, + totalPackets: chunks.length, + tgui: 1, + window_id: Byond.windowId, + }; + Byond.topic(message); + } + + return; + } + } + + // Append an identifying header + assign(message, { + tgui: 1, + window_id: Byond.windowId, + }); + Byond.topic(message); + }; + + // This function exists purely for debugging, do not use it in code! + Byond.injectMessage = function (type, payload) { + window.update(JSON.stringify({ type: type, payload: payload })); + }; + + Byond.subscribe = function (listener) { + window.update.flushQueue(listener); + window.update.listeners.push(listener); + }; + + Byond.subscribeTo = function (type, listener) { + let _listener = function (_type, payload) { + if (_type === type) { + listener(payload); + } + }; + window.update.flushQueue(_listener); + window.update.listeners.push(_listener); + }; + + // Asset loaders + // ------------------------------------------------------ + + let RETRY_ATTEMPTS = 5; + let RETRY_WAIT_INITIAL = 500; + let RETRY_WAIT_INCREMENT = 500; + + let loadedAssetByUrl = {}; + + let isStyleSheetLoaded = function (node, url) { + let styleSheet = node.sheet; + if (styleSheet) { + return styleSheet.rules.length > 0; + } + return false; + }; + + let injectNode = function (node) { + if (!document.body) { + setTimeout(() => { + injectNode(node); + }); + return; + } + let refs = document.body.childNodes; + let ref = refs[refs.length - 1]; + ref.parentNode.insertBefore(node, ref.nextSibling); + }; + + let loadAsset = function (options) { + let url = options.url; + let type = options.type; + let sync = options.sync; + let attempt = options.attempt || 0; + if (loadedAssetByUrl[url]) { + return; + } + loadedAssetByUrl[url] = options; + // Generic retry function + let retry = function () { + if (attempt >= RETRY_ATTEMPTS) { + let errorMessage = + 'Error: Failed to load the asset ' + + "'" + + url + + "' after several attempts."; + if (type === 'css') { + errorMessage += + +'\nStylesheet was either not found, ' + + "or you're trying to load an empty stylesheet " + + 'that has no CSS rules in it.'; + } + throw new Error(errorMessage); + } + setTimeout( + () => { + loadedAssetByUrl[url] = null; + options.attempt += 1; + loadAsset(options); + }, + RETRY_WAIT_INITIAL + attempt * RETRY_WAIT_INCREMENT + ); + }; + // JS specific code + if (type === 'js') { + let node = document.createElement('script'); + node.type = 'text/javascript'; + node.crossOrigin = 'anonymous'; + node.src = url; + if (sync) { + node.defer = true; + } else { + node.async = true; + } + node.onerror = function () { + node.onerror = null; + node.parentNode.removeChild(node); + node = null; + retry(); + }; + injectNode(node); + return; + } + // CSS specific code + if (type === 'css') { + let node = document.createElement('link'); + node.type = 'text/css'; + node.rel = 'stylesheet'; + node.crossOrigin = 'anonymous'; + node.href = url; + // Temporarily set media to something inapplicable + // to ensure it'll fetch without blocking render + if (!sync) { + node.media = 'only x'; + } + let removeNodeAndRetry = function () { + node.parentNode.removeChild(node); + node = null; + retry(); + }; + // 516: Chromium won't call onload() if there is a 404 error + // Legacy IE doesn't use onerror, so we retain that + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#stylesheet_load_events + node.onerror = function () { + node.onerror = null; + removeNodeAndRetry(); + }; + node.onload = function () { + node.onload = null; + if (isStyleSheetLoaded(node, url)) { + // Render the stylesheet + node.media = 'all'; + return; + } + removeNodeAndRetry(); + }; + injectNode(node); + return; + } + }; + + Byond.loadJs = function (url, sync) { + loadAsset({ url: url, sync: sync, type: 'js' }); + }; + + Byond.loadCss = function (url, sync) { + loadAsset({ url: url, sync: sync, type: 'css' }); + }; + + Byond.saveBlob = function (blob, filename, ext) { + if (window.navigator.msSaveBlob) { + window.navigator.msSaveBlob(blob, filename); + } else if (window.showSaveFilePicker) { + let accept = {}; + accept[blob.type] = [ext]; + + let opts = { + suggestedName: filename, + types: [ + { + description: 'SS13 file', + accept: accept, + }, + ], + }; + + try { + window + .showSaveFilePicker(opts) + .then((fileHandle) => { + fileHandle + .createWritable() + .then((writeableFileHandle) => { + writeableFileHandle + .write(blob) + .then(() => { + writeableFileHandle.close(); + }) + .catch((e) => { + console.error(e); + }); + }) + .catch((e) => { + console.error(e); + }); + }) + .catch((e) => { + console.error(e); + }); + } catch (e) { + console.error(e); + } + } + }; + + // Icon cache + Byond.iconRefMap = {}; +})(); + +// Error handling +// ------------------------------------------------------ + +window.onerror = function (msg, url, line, col, error) { + window.onerror.errorCount = (window.onerror.errorCount || 0) + 1; + // Proper stacktrace + let stack = error && error.stack; + // Ghetto stacktrace + if (!stack) { + stack = msg + '\n at ' + url + ':' + line; + if (col) { + stack += ':' + col; + } + } + // Augment the stack + stack = window.__augmentStack__(stack, error); + // Print error to the page + if (Byond.strictMode) { + let errorRoot = document.getElementById('FatalError'); + let errorStack = document.getElementById('FatalError__stack'); + if (errorRoot) { + errorRoot.className = 'FatalError FatalError--visible'; + if (window.onerror.__stack__) { + window.onerror.__stack__ += '\n\n' + stack; + } else { + window.onerror.__stack__ = stack; + } + let textProp = 'textContent'; + errorStack[textProp] = window.onerror.__stack__; + } + // Set window geometry + let setFatalErrorGeometry = function () { + Byond.winset(Byond.windowId, { + titlebar: true, + 'is-visible': true, + 'can-resize': true, + }); + }; + setFatalErrorGeometry(); + setInterval(setFatalErrorGeometry, 1000); + } + // Send logs to the game server + if (Byond.strictMode) { + Byond.sendMessage({ + type: 'log', + fatal: 1, + message: stack, + }); + } else if (window.onerror.errorCount <= 1) { + stack += '\nWindow is in non-strict mode, future errors are suppressed.'; + Byond.sendMessage({ + type: 'log', + message: stack, + }); + } + // Short-circuit further updates + if (Byond.strictMode) { + window.update = function () {}; + window.update.queue = []; + } + // Prevent default action + return true; +}; + +// Catch unhandled promise rejections +window.onunhandledrejection = function (e) { + let msg = 'UnhandledRejection'; + if (e.reason) { + msg += ': ' + (e.reason.message || e.reason.description || e.reason); + if (e.reason.stack) { + e.reason.stack = 'UnhandledRejection: ' + e.reason.stack; + } + } + window.onerror(msg, null, null, null, e.reason); +}; + +// Helper for augmenting stack traces on fatal errors +window.__augmentStack__ = function (stack, error) { + return stack + '\nUser Agent: ' + navigator.userAgent; +}; + +// Incoming message handling +// ------------------------------------------------------ + +// Message handler +window.update = function (rawMessage) { + // Push onto the queue (active during initialization) + if (window.update.queueActive) { + window.update.queue.push(rawMessage); + return; + } + // Parse the message + let message = Byond.parseJson(rawMessage); + // Notify listeners + let listeners = window.update.listeners; + for (let i = 0; i < listeners.length; i++) { + listeners[i](message.type, message.payload); + } +}; + +// Properties and variables of this specific handler +window.update.listeners = []; +window.update.queue = []; +window.update.queueActive = true; +window.update.flushQueue = function (listener) { + // Disable and clear the queue permanently on short delay + if (window.update.queueActive) { + window.update.queueActive = false; + if (window.setTimeout) { + window.setTimeout(() => { + window.update.queue = []; + }, 0); + } + } + // Process queued messages on provided listener + let queue = window.update.queue; + for (let i = 0; i < queue.length; i++) { + let message = Byond.parseJson(queue[i]); + listener(message.type, message.payload); + } +}; + +window.replaceHtml = function (inline_html) { + let children = document.body.childNodes; + + for (let i = 0; i < children.length; i++) { + if (children[i].nodeValue == ' tgui:inline-html-start ') { + while (children[i].nodeValue != ' tgui:inline-html-end ') { + children[i].remove(); + } + children[i].remove(); + } + } + + document.body.insertAdjacentHTML( + 'afterbegin', + '' + + inline_html + + '' + ); +}; diff --git a/tgui/packages/tgui-setup/ntos-error.css b/tgui/packages/tgui-setup/ntos-error.css new file mode 100644 index 0000000000..70bf334e23 --- /dev/null +++ b/tgui/packages/tgui-setup/ntos-error.css @@ -0,0 +1,112 @@ +/* Blink polyfill, originally authored on /tg/ by @iamgoofball */ +blink { + animation: 1s linear infinite condemned-blink-effect; +} + +@keyframes condemned-blink-effect { + 0% { + visibility: hidden; + } + 50% { + visibility: hidden; + } + 100% { + visibility: visible; + } +} + +.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; +} + +.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; + } +} diff --git a/tgui/packages/tgui-setup/package.json b/tgui/packages/tgui-setup/package.json new file mode 100644 index 0000000000..e5ca9cd691 --- /dev/null +++ b/tgui/packages/tgui-setup/package.json @@ -0,0 +1,14 @@ +{ + "name": "tgui-setup", + "version": "0.0.1", + "description": "Minifies assets to inject into tgui.html", + "private": true, + "scripts": { + "build:helpers": "terser helpers.js -f ascii_only,comments=false -o ../../public/helpers.min.js", + "build:style": "cleancss -o ../../public/ntos-error.min.css ntos-error.css" + }, + "dependencies": { + "clean-css-cli": "^5.6.3", + "terser": "^5.39.0" + } +} diff --git a/tgui/packages/tgui/interfaces/VolumePanel.tsx b/tgui/packages/tgui/interfaces/VolumePanel.tsx index 94f0f86c93..8228b2ef71 100644 --- a/tgui/packages/tgui/interfaces/VolumePanel.tsx +++ b/tgui/packages/tgui/interfaces/VolumePanel.tsx @@ -1,6 +1,12 @@ import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; -import { Button, LabeledList, Section, Slider } from 'tgui-core/components'; +import { + Button, + LabeledList, + Section, + Slider, + Stack, +} from 'tgui-core/components'; type Data = { volume_channels: { key; val: number }[]; @@ -12,26 +18,34 @@ export const VolumePanel = (props) => { const { volume_channels } = data; return ( - + -
+
{Object.keys(volume_channels).map((key) => ( - - act('adjust_volume', { channel: key, vol: val / 100 }) - } - /> -