diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 8595f23aa727..4695e236c692 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -224,6 +224,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
///////////
/client/New(TopicData)
+ if(byond_version >= 516) // Enable 516 compat browser storage mechanisms
+ winset(src, "", "browser-options=byondstorage,find")
+
var/tdata = TopicData //save this for later use
TopicData = null //Prevent calls to client.Topic from connect
diff --git a/code/modules/photography/photos/photo.dm b/code/modules/photography/photos/photo.dm
index 94ade197e2f1..e2b8212785ba 100644
--- a/code/modules/photography/photos/photo.dm
+++ b/code/modules/photography/photos/photo.dm
@@ -92,7 +92,7 @@
user << browse_rsc(picture.picture_image, "tmp_photo.png")
user << browse("
[name]" \
+ "" \
- + "" \
+ + "" \
+ "[scribble ? "
Written on the back:
[scribble]" : ""]"\
+ "", "window=photo_showing;size=480x608")
onclose(user, "[name]")
diff --git a/code/modules/tgui_panel/external.dm b/code/modules/tgui_panel/external.dm
index 3e7dbc3178f2..2cd550d237b3 100644
--- a/code/modules/tgui_panel/external.dm
+++ b/code/modules/tgui_panel/external.dm
@@ -35,6 +35,8 @@
// Force show the panel to see if there are any errors
winset(src, "output", "is-disabled=1&is-visible=0")
winset(src, "browseroutput", "is-disabled=0;is-visible=1")
+ if(byond_version >= 516)
+ winset(src, null, "browser-options=byondstorage,find")
/client/verb/refresh_tgui()
set name = "Refresh TGUI"
diff --git a/html/browser/common.css b/html/browser/common.css
index 6a37b98039e5..43bb7290bbd2 100644
--- a/html/browser/common.css
+++ b/html/browser/common.css
@@ -11,7 +11,8 @@ body
hr
{
background-color: #40628a;
- height: 1px;
+ height: 2px;
+ border: 0;
}
a, button, a:link, a:visited, a:active, .linkOn, .linkOff
diff --git a/html/statbrowser.css b/html/statbrowser.css
index dc693f42f756..85aebe339c4b 100644
--- a/html/statbrowser.css
+++ b/html/statbrowser.css
@@ -1,3 +1,8 @@
+.light:root {
+ --scrollbar-base: #f2f2f2;
+ --scrollbar-thumb: #a7a7a7;
+}
+
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 12px !important;
@@ -7,6 +12,11 @@ body {
overflow-y: scroll;
}
+.dark:root {
+ --scrollbar-base: #181818;
+ --scrollbar-thumb: #363636;
+}
+
body.dark {
background-color: #131313;
color: #b2c4dd;
diff --git a/tgui/packages/common/storage.js b/tgui/packages/common/storage.js
index acf842f64083..9a47ecc2b54c 100644
--- a/tgui/packages/common/storage.js
+++ b/tgui/packages/common/storage.js
@@ -7,7 +7,7 @@
*/
export const IMPL_MEMORY = 0;
-export const IMPL_LOCAL_STORAGE = 1;
+export const IMPL_HUB_STORAGE = 1;
export const IMPL_INDEXED_DB = 2;
const INDEXED_DB_VERSION = 1;
@@ -25,14 +25,11 @@ const testGeneric = (testFn) => () => {
}
};
-// Localstorage can sometimes throw an error, even if DOM storage is not
-// disabled in IE11 settings.
-// See: https://superuser.com/questions/1080011
-// prettier-ignore
-const testLocalStorage = testGeneric(() => (
- window.localStorage && window.localStorage.getItem
-));
+const testHubStorage = testGeneric(
+ () => window.hubStorage && window.hubStorage.getItem,
+);
+// TODO: Remove with 516
// prettier-ignore
const testIndexedDb = testGeneric(() => (
(window.indexedDB || window.msIndexedDB)
@@ -45,49 +42,50 @@ class MemoryBackend {
this.store = {};
}
- get(key) {
+ async get(key) {
return this.store[key];
}
- set(key, value) {
+ async set(key, value) {
this.store[key] = value;
}
- remove(key) {
+ async remove(key) {
this.store[key] = undefined;
}
- clear() {
+ async clear() {
this.store = {};
}
}
-class LocalStorageBackend {
+class HubStorageBackend {
constructor() {
- this.impl = IMPL_LOCAL_STORAGE;
+ this.impl = IMPL_HUB_STORAGE;
}
- get(key) {
- const value = localStorage.getItem(key);
+ async get(key) {
+ const value = await window.hubStorage.getItem(key);
if (typeof value === 'string') {
return JSON.parse(value);
}
}
set(key, value) {
- localStorage.setItem(key, JSON.stringify(value));
+ window.hubStorage.setItem(key, JSON.stringify(value));
}
remove(key) {
- localStorage.removeItem(key);
+ window.hubStorage.removeItem(key);
}
clear() {
- localStorage.clear();
+ window.hubStorage.clear();
}
}
class IndexedDbBackend {
+ // TODO: Remove with 516
constructor() {
this.impl = IMPL_INDEXED_DB;
/** @type {Promise} */
@@ -108,7 +106,7 @@ class IndexedDbBackend {
});
}
- getStore(mode) {
+ async getStore(mode) {
// prettier-ignore
return this.dbPromise.then((db) => db
.transaction(INDEXED_DB_STORE_NAME, mode)
@@ -125,13 +123,6 @@ class IndexedDbBackend {
}
async set(key, value) {
- // The reason we don't _save_ null is because IE 10 does
- // not support saving the `null` type in IndexedDB. How
- // ironic, given the bug below!
- // See: https://github.com/mozilla/localForage/issues/161
- if (value === null) {
- value = undefined;
- }
// NOTE: We deliberately make this operation transactionless
const store = await this.getStore(READ_WRITE);
store.put(value, key);
@@ -157,6 +148,10 @@ class IndexedDbBackend {
class StorageProxy {
constructor() {
this.backendPromise = (async () => {
+ if (!Byond.TRIDENT && testHubStorage()) {
+ return new HubStorageBackend();
+ }
+ // TODO: Remove with 516
if (testIndexedDb()) {
try {
const backend = new IndexedDbBackend();
@@ -164,9 +159,9 @@ class StorageProxy {
return backend;
} catch {}
}
- if (testLocalStorage()) {
- return new LocalStorageBackend();
- }
+ console.warn(
+ 'No supported storage backend found. Using in-memory storage.',
+ );
return new MemoryBackend();
})();
}
diff --git a/tgui/packages/tgui-panel/chat/renderer.jsx b/tgui/packages/tgui-panel/chat/renderer.jsx
index 8a9fea109eb4..e23ed85a1102 100644
--- a/tgui/packages/tgui-panel/chat/renderer.jsx
+++ b/tgui/packages/tgui-panel/chat/renderer.jsx
@@ -163,7 +163,7 @@ class ChatRenderer {
// Find scrollable parent
this.scrollNode = findNearestScrollableParent(this.rootNode);
this.scrollNode.addEventListener('scroll', this.handleScroll);
- setImmediate(() => {
+ setTimeout(() => {
this.scrollToBottom();
});
// Flush the queue
@@ -473,7 +473,7 @@ class ChatRenderer {
this.rootNode.appendChild(fragment);
}
if (this.scrollTracking) {
- setImmediate(() => this.scrollToBottom());
+ setTimeout(() => this.scrollToBottom());
}
}
// Notify listeners that we have processed the batch
diff --git a/tgui/packages/tgui-panel/panelFocus.js b/tgui/packages/tgui-panel/panelFocus.js
index b7cea2293149..8cff4a361b3e 100644
--- a/tgui/packages/tgui-panel/panelFocus.js
+++ b/tgui/packages/tgui-panel/panelFocus.js
@@ -15,7 +15,7 @@ import { focusMap } from 'tgui/focus';
// text you can select with the mouse.
const MIN_SELECTION_DISTANCE = 10;
-const deferredFocusMap = () => setImmediate(() => focusMap());
+const deferredFocusMap = () => setTimeout(() => focusMap());
export const setupPanelFocusHacks = () => {
let focusStolen = false;
diff --git a/tgui/packages/tgui-say/TguiSay.tsx b/tgui/packages/tgui-say/TguiSay.tsx
index 22a51e7de561..0a1c9c7e4efa 100644
--- a/tgui/packages/tgui-say/TguiSay.tsx
+++ b/tgui/packages/tgui-say/TguiSay.tsx
@@ -24,6 +24,13 @@ type State = {
const CHANNEL_REGEX = /^[:.]\w\s/;
+const ROWS: Record = {
+ small: 1,
+ medium: 2,
+ large: 3,
+ width: 1, // not used
+} as const;
+
export class TguiSay extends Component<{}, State> {
private channelIterator: ChannelIterator;
private chatHistory: ChatHistory;
@@ -327,11 +334,14 @@ export class TguiSay extends Component<{}, State> {
{this.state.buttonContent}
diff --git a/tgui/packages/tgui-say/index.tsx b/tgui/packages/tgui-say/index.tsx
index ed512b525ff5..230571c76d9b 100644
--- a/tgui/packages/tgui-say/index.tsx
+++ b/tgui/packages/tgui-say/index.tsx
@@ -1,19 +1,11 @@
import './styles/main.scss';
-import { createRenderer } from 'tgui/renderer';
-import { TguiSay } from './TguiSay';
+import { render } from 'inferno';
-const renderApp = createRenderer(() => {
- return ;
-});
+import { TguiSay } from './TguiSay';
-const setupApp = () => {
- // Delay setup
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', setupApp);
- return;
- }
+document.onreadystatechange = function () {
+ if (document.readyState !== 'complete') return;
- renderApp();
+ const root = document.getElementById('react-root');
+ render(, root);
};
-
-setupApp();
diff --git a/tgui/packages/tgui-say/styles/button.scss b/tgui/packages/tgui-say/styles/button.scss
index 89e1cceca943..202a69f70182 100644
--- a/tgui/packages/tgui-say/styles/button.scss
+++ b/tgui/packages/tgui-say/styles/button.scss
@@ -19,6 +19,13 @@
}
}
+// Remove conditionals with 516
+@supports (not (-webkit-hyphens: none)) and (not (-moz-appearance: none)) {
+ .button {
+ outline: none;
+ }
+}
+
.button-lightMode {
background-color: colors.$lightBorder;
border: none;
diff --git a/tgui/packages/tgui-say/styles/colors.scss b/tgui/packages/tgui-say/styles/colors.scss
index b5de439d052a..3e773aa3627f 100644
--- a/tgui/packages/tgui-say/styles/colors.scss
+++ b/tgui/packages/tgui-say/styles/colors.scss
@@ -5,6 +5,7 @@ $button: #1f1f1f;
$lightMode: #ffffff;
$lightBorder: #bbbbbb;
$lightHover: #eaeaea;
+$scrollbar-color-multiplier: 1 !default;
$_channel_map: (
'Admin': #ffbbff,
diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts
index 6c1fdd335e4d..2e4ca9e13425 100644
--- a/tgui/packages/tgui/backend.ts
+++ b/tgui/packages/tgui/backend.ts
@@ -176,7 +176,7 @@ export const backendMiddleware = (store) => {
Byond.winset(Byond.windowId, {
'is-visible': false,
});
- setImmediate(() => focusMap());
+ setTimeout(() => focusMap());
}
if (type === 'backend/update') {
@@ -206,7 +206,7 @@ export const backendMiddleware = (store) => {
setupDrag();
// We schedule this for the next tick here because resizing and unhiding
// during the same tick will flash with a white background.
- setImmediate(() => {
+ setTimeout(() => {
perf.mark('resume/start');
// Doublecheck if we are not re-suspended.
const { suspended } = selectBackend(store.getState());
diff --git a/tgui/packages/tgui/components/BodyZoneSelector.tsx b/tgui/packages/tgui/components/BodyZoneSelector.tsx
index 747aca662e1b..3c8333c9a055 100644
--- a/tgui/packages/tgui/components/BodyZoneSelector.tsx
+++ b/tgui/packages/tgui/components/BodyZoneSelector.tsx
@@ -113,6 +113,7 @@ export class BodyZoneSelector extends Component<
}}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
position: 'absolute',
width: `${32 * scale}px`,
height: `${32 * scale}px`,
@@ -125,6 +126,7 @@ export class BodyZoneSelector extends Component<
src={resolveAsset(`body_zones.${selectedZone}.png`)}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
'pointer-events': 'none',
position: 'absolute',
width: `${32 * scale}px`,
@@ -139,6 +141,7 @@ export class BodyZoneSelector extends Component<
src={resolveAsset(`body_zones.${hoverZone}.png`)}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
opacity: 0.5,
'pointer-events': 'none',
position: 'absolute',
diff --git a/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx b/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx
index c0697703b44b..48b28d1cbba1 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoBloodsucker.tsx
@@ -197,6 +197,7 @@ const BloodsuckerClan = (props: any) => {
src={resolveAsset(`bloodsucker.${ClanInfo.clan_icon}.png`)}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
position: 'absolute',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/ArmamentStation.jsx b/tgui/packages/tgui/interfaces/ArmamentStation.jsx
index 331a60491ef1..ee8198f3781b 100644
--- a/tgui/packages/tgui/interfaces/ArmamentStation.jsx
+++ b/tgui/packages/tgui/interfaces/ArmamentStation.jsx
@@ -120,6 +120,7 @@ export const ArmamentStation = (props) => {
'horizontal-align': 'middle',
'-ms-interpolation-mode':
'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/CargoImportConsole.jsx b/tgui/packages/tgui/interfaces/CargoImportConsole.jsx
index 409ade80de89..caf60afba188 100644
--- a/tgui/packages/tgui/interfaces/CargoImportConsole.jsx
+++ b/tgui/packages/tgui/interfaces/CargoImportConsole.jsx
@@ -115,6 +115,7 @@ export const CargoImportConsole = (props) => {
'horizontal-align': 'middle',
'-ms-interpolation-mode':
'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/FishingRod.tsx b/tgui/packages/tgui/interfaces/FishingRod.tsx
index d6739bc2f373..d6d62dfe7a4f 100644
--- a/tgui/packages/tgui/interfaces/FishingRod.tsx
+++ b/tgui/packages/tgui/interfaces/FishingRod.tsx
@@ -35,6 +35,7 @@ const FishingRodSlot = (props: FishingSlotProps) => {
src={`data:image/jpeg;base64,${icon}`}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
'vertical-align': 'middle',
'object-fit': 'cover',
}}
diff --git a/tgui/packages/tgui/interfaces/GreyscaleModifyMenu.tsx b/tgui/packages/tgui/interfaces/GreyscaleModifyMenu.tsx
index 1a36505d5b6a..bfa98fd3c57a 100644
--- a/tgui/packages/tgui/interfaces/GreyscaleModifyMenu.tsx
+++ b/tgui/packages/tgui/interfaces/GreyscaleModifyMenu.tsx
@@ -220,7 +220,10 @@ const PreviewDisplay = (props) => {
m={0}
width="75%"
mx="10%"
- style={{ '-ms-interpolation-mode': 'nearest-neighbor' }}
+ style={{
+ '-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
+ }}
/>
) : (
@@ -230,7 +233,10 @@ const PreviewDisplay = (props) => {
name="image"
ml="25%"
size={5}
- style={{ '-ms-interpolation-mode': 'nearest-neighbor' }}
+ style={{
+ '-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
+ }}
/>
@@ -282,7 +288,10 @@ const SingleSprite = (props) => {
as="img"
src={source}
width="100%"
- style={{ '-ms-interpolation-mode': 'nearest-neighbor' }}
+ style={{
+ '-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
+ }}
/>
);
};
diff --git a/tgui/packages/tgui/interfaces/NtosEmojipedia.jsx b/tgui/packages/tgui/interfaces/NtosEmojipedia.jsx
index 8a2402f59ff5..1cc19682366b 100644
--- a/tgui/packages/tgui/interfaces/NtosEmojipedia.jsx
+++ b/tgui/packages/tgui/interfaces/NtosEmojipedia.jsx
@@ -48,6 +48,7 @@ export const NtosEmojipedia = (props) => {
title={emoji.name}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
onClick={() => {
new Promise((resolve, _) => {
diff --git a/tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx b/tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx
index 3f0d2c9e6798..ba58a33137b4 100644
--- a/tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx
+++ b/tgui/packages/tgui/interfaces/NtosPortraitPrinter.jsx
@@ -60,6 +60,7 @@ export const NtosPortraitPrinter = (props) => {
style={{
'vertical-align': 'middle',
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/OutfitEditor.jsx b/tgui/packages/tgui/interfaces/OutfitEditor.jsx
index d32c5420fd46..ea9a6450140b 100644
--- a/tgui/packages/tgui/interfaces/OutfitEditor.jsx
+++ b/tgui/packages/tgui/interfaces/OutfitEditor.jsx
@@ -18,6 +18,7 @@ export const OutfitEditor = (props) => {
src={`data:image/jpeg;base64,${dummy64}`}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
{
title={currItem?.desc}
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
{
style={{
'vertical-align': 'middle',
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/SelectEquipment.jsx b/tgui/packages/tgui/interfaces/SelectEquipment.jsx
index f29d48329fe1..87bba7f54cae 100644
--- a/tgui/packages/tgui/interfaces/SelectEquipment.jsx
+++ b/tgui/packages/tgui/interfaces/SelectEquipment.jsx
@@ -101,6 +101,7 @@ export const SelectEquipment = (props) => {
height="100%"
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/StripMenu.tsx b/tgui/packages/tgui/interfaces/StripMenu.tsx
index 84173bf6f9a8..609633990c15 100644
--- a/tgui/packages/tgui/interfaces/StripMenu.tsx
+++ b/tgui/packages/tgui/interfaces/StripMenu.tsx
@@ -303,6 +303,7 @@ export const StripMenu = (props) => {
width="100%"
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
'vertical-align': 'middle',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/Trophycase.jsx b/tgui/packages/tgui/interfaces/Trophycase.jsx
index e8d8292a3c32..0387c4015243 100644
--- a/tgui/packages/tgui/interfaces/Trophycase.jsx
+++ b/tgui/packages/tgui/interfaces/Trophycase.jsx
@@ -130,6 +130,7 @@ const ShowpieceImage = (props) => {
width="96px"
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
}}
/>
diff --git a/tgui/packages/tgui/interfaces/Vendatray.tsx b/tgui/packages/tgui/interfaces/Vendatray.tsx
index ad5bdf466a2b..fbd950477cbc 100644
--- a/tgui/packages/tgui/interfaces/Vendatray.tsx
+++ b/tgui/packages/tgui/interfaces/Vendatray.tsx
@@ -93,6 +93,7 @@ const VendingImage = (props) => {
width="96px"
style={{
'-ms-interpolation-mode': 'nearest-neighbor',
+ 'image-rendering': 'pixelated',
'vertical-align': 'middle',
}}
/>
diff --git a/tgui/packages/tgui/stories/ByondUi.stories.jsx b/tgui/packages/tgui/stories/ByondUi.stories.jsx
index fcaa42f81eb2..c284d66b70f6 100644
--- a/tgui/packages/tgui/stories/ByondUi.stories.jsx
+++ b/tgui/packages/tgui/stories/ByondUi.stories.jsx
@@ -34,7 +34,7 @@ const Story = (props) => {